İçeriğe geç
academia.sh

Ders 04 / 22

Denge ve Hizalama

Hizalamanın paylaşılan görünmez eksen olarak tanımlanması; eksen sayısının ve alan ağırlıklı yatay dengenin hesaplanması, optik hizalama ile düzensiz sağ kenarın ölçülmesi.

İçindekiler

Buraya kadar her karar tek tek ögeler üzerinden verildi: bu öge ne kadar büyük, bu ikisi ne kadar yakın, bu düğme ne kadar ağır. Bir ekranı bir arada tutan şey ise ögelerin kendileri değil, aralarındaki görünmez çizgilerdir. Katalog sayfasında süzgeç panelinin sol kenarı ile sayfa başlığının sol kenarı aynı hizadaysa, aralarında hiçbir çizgi olmamasına rağmen bir ilişki kurulmuş olur.

Bu ders üç soruyu sayıya bağlar: bir düzende kaç hizalama ekseni vardır, ekrandaki ağırlık yatay eksende nasıl dağılmıştır ve bir metin bloğunun sağ kenarı ne kadar düzensizdir.

Hizalama Paylaşılan Bir Eksendir

Hizalama (alignment), birden çok ögenin aynı görünmez çizgiyi paylaşmasıdır. Bu çizgi ögelerin sol kenarı, sağ kenarı, dikey orta ekseni ya da metin taban çizgisi olabilir. Terim, Bilgisayarlar Nasıl Çalışır kursunda bellek adreslerinin sözcük sınırına oturması anlamında da geçmişti; burada kastedilen görsel eksendir, iki kullanım arasındaki tek ortak nokta “ortak bir sınıra oturma” fikridir.

Hizalamanın işlevi ilişki kurmaktır. Yakınlık ilkesi ögeleri birbirine bağlarken boşluk harcar; hizalama hiçbir şey harcamadan aynı işi uzak ögeler arasında yapar. Ekranın en üstündeki sayfa başlığı ile en altındaki dipnot aynı sol eksende duruyorsa, aralarında 500 piksel olsa bile aynı sütuna ait oldukları okunur.

Bunun karşılığında hizalama bir maliyet üretir: her yeni eksen bir kural daha demektir. Ekranda kaç farklı sol kenar varsa, okur o kadar farklı başlangıç noktası öğrenmek zorundadır. Eksen sayısı bu yüzden doğrudan ölçülebilir bir düzen niteliğidir.

Eksen Sayısı ve Yatay Denge

Aşağıdaki program katalog sonuç sayfasının bloklarını alır, kaç ayrı sol ve sağ eksen kullanıldığını sayar ve düzenin alan ağırlıklı yatay merkezini hesaplar. İkinci ölçü dengeyi verir: her bloğun alanı ağırlık kabul edilir, blokların ortak ağırlık merkezi bulunur ve kapsayıcının orta ekseninden sapması ölçülür.

// 04-hizalama.mjs — hizalama ekseni sayisi ve alan agirlikli yatay denge

const KAPSAYICI = 1200;

// Katalog sonuc sayfasinin bloklari: (ad, sol kenar, genislik, yukseklik)
const oncesi = [
  { ad: "sayfa basligi", x: 24, g: 600, y: 40 },
  { ad: "arama alani", x: 32, g: 520, y: 44 },
  { ad: "suzgec paneli", x: 24, g: 264, y: 480 },
  { ad: "suzgec basligi", x: 40, g: 232, y: 24 },
  { ad: "sonuc listesi", x: 312, g: 600, y: 480 },
  { ad: "kayit basligi", x: 328, g: 560, y: 24 },
  { ad: "kayit ustverisi", x: 336, g: 540, y: 18 },
];

const sonrasi = [
  { ad: "sayfa basligi", x: 24, g: 1152, y: 40 },
  { ad: "arama alani", x: 24, g: 1152, y: 44 },
  { ad: "suzgec paneli", x: 24, g: 264, y: 480 },
  { ad: "suzgec basligi", x: 24, g: 264, y: 24 },
  { ad: "sonuc listesi", x: 312, g: 864, y: 480 },
  { ad: "kayit basligi", x: 312, g: 864, y: 24 },
  { ad: "kayit ustverisi", x: 312, g: 864, y: 18 },
];

function eksenler(bloklar) {
  const sol = new Set(bloklar.map((b) => b.x));
  const sag = new Set(bloklar.map((b) => b.x + b.g));
  return { sol: [...sol].sort((a, b) => a - b), sag: [...sag].sort((a, b) => a - b) };
}

function denge(bloklar) {
  const toplamAlan = bloklar.reduce((t, b) => t + b.g * b.y, 0);
  const moment = bloklar.reduce((t, b) => t + b.g * b.y * (b.x + b.g / 2), 0);
  const merkez = moment / toplamAlan;
  const sapma = merkez - KAPSAYICI / 2;
  return { merkez, sapma, oran: (100 * Math.abs(sapma)) / (KAPSAYICI / 2) };
}

function rapor(ad, bloklar) {
  const e = eksenler(bloklar);
  const d = denge(bloklar);
  console.log(ad);
  console.log(`  sol eksenler (${e.sol.length}): ${e.sol.join(", ")}`);
  console.log(`  sag eksenler (${e.sag.length}): ${e.sag.join(", ")}`);
  console.log(`  toplam eksen sayisi: ${e.sol.length + e.sag.length}`);
  console.log(
    `  alan agirlikli merkez: ${d.merkez.toFixed(1)}px   kapsayici merkezi: ${KAPSAYICI / 2}px`
  );
  console.log(`  sapma: ${d.sapma.toFixed(1)}px   yari genisligin ${d.oran.toFixed(1)}%'i`);
  console.log("");
}

rapor("oncesi", oncesi);
rapor("sonrasi", sonrasi);
oncesi
  sol eksenler (6): 24, 32, 40, 312, 328, 336
  sag eksenler (7): 272, 288, 552, 624, 876, 888, 912
  toplam eksen sayisi: 13
  alan agirlikli merkez: 459.7px   kapsayici merkezi: 600px
  sapma: -140.3px   yari genisligin 23.4%'i

sonrasi
  sol eksenler (2): 24, 312
  sag eksenler (2): 288, 1176
  toplam eksen sayisi: 4
  alan agirlikli merkez: 608.6px   kapsayici merkezi: 600px
  sapma: 8.6px   yari genisligin 1.4%'i

“Öncesi” düzeni tek tek bakıldığında kusursuz görünür: her blok kendi kabının içinde düzgün durur, iç boşluklar makul, hiçbir öge taşmaz. Sayı başka bir şey söylüyor. Yedi blok on üç ayrı eksen kullanıyor; sol kenarlar 24, 32, 40, 312, 328 ve 336 piksellerde başlıyor. Bu değerlerin hiçbiri hatalı değildir; sorun, hepsinin aynı anda var olmasıdır. 8 piksellik farklar hizalama olarak da algılanmaz, kasıtlı girinti olarak da algılanmaz; yalnızca kenarı bulanıklaştırır.

“Sonrası” düzeni aynı bilgiyi dört eksende taşır. Kayıt başlığı artık kendi kabının iç boşluğuyla değil, sonuç listesinin sol ekseniyle hizalıdır. İç boşluk yok olmadı; listenin dışına, blokların arasına taşındı.

İkinci ölçü de ilk düzenin bir başka kusurunu gösteriyor. Alan ağırlıklı merkez 459.7 pikselde, kapsayıcının orta ekseninden 140.3 piksel solda. Sağ tarafta 288 piksel genişliğinde bir boşluk şeridi vardır ve bu şerit hiçbir işlev taşımaz. Sonuç listesi kapsayıcının sağ kenarına kadar genişletildiğinde sapma 8.6 piksele düşer.

Sıfır Sapma Hedef Değildir

Denge ölçüsünün amacı simetri dayatmak değildir. Simetrik bir düzen dengelidir ama her dengeli düzen simetrik değildir: solda dar ve uzun bir panel, sağda geniş ve kısa bir blok bulunan asimetrik bir düzen de sıfıra yakın sapma verebilir.

Ölçünün işlevi, niyetsiz dengesizliği yakalamaktır. Yüzde 23’lük bir sapma çoğu zaman bir karar değil, bir artıktır: içerik sağa kadar uzatılmamış, kapsayıcı genişliği değiştirilmiş ama bloklar güncellenmemiştir. Kurs boyunca kullanılacak ölçüt şudur: yarı genişliğin yüzde 10’unu aşan sapma gerekçelendirilir. Gerekçe varsa sapma kalır — bir okuma sütunu bilerek sola yaslanmış olabilir, sağdaki boşluk bilerek ayrılmış olabilir. Gerekçe yoksa düzeltilir.

Optik Hizalama

Matematiksel hizalama her zaman algılanan hizalamayı vermez. Kenarları düz olmayan biçimler, kutuları hizalandığında hizasız görünür. Bu farkı düzeltmeye optik hizalama (optical alignment) denir.

Üç yaygın durum vardır. Birincisi yuvarlak biçimlerdir: bir daire, kare ile aynı kutuya oturduğunda daha küçük görünür, çünkü kenarları kutuya yalnız dört noktada değer. İkincisi noktalama işaretleridir: tırnakla başlayan bir satır, tırnağın açık alanı yüzünden bir adım içeride görünür. Üçüncüsü üçgen biçimlerdir: sağa bakan bir üçgen, kutusunun tam ortasına konduğunda sola kaymış görünür, çünkü kütlesi tabanında toplanmıştır.

Bu üç durumun ortak yanı, düzeltmenin ölçülemez değil hesaplanamaz olmasıdır: fark gerçektir ama biçime özgüdür ve genel bir formülü yoktur. Uygulanabilir kural şudur: optik düzeltme yalnız kenarı düz olmayan biçimlere, yalnız büyük boyutlarda ve yalnız kaydırma miktarı belgelenerek uygulanır. Belgelenmemiş optik düzeltmeler, bir sonraki derste ele alınacak tutarlılık sorununun en sık kaynağıdır: kimse o 2 pikselin neden orada olduğunu bilmez, kimse silmeye cesaret edemez.

Metnin Sağ Kenarı

Bir metin bloğunun sol kenarı bir eksendir; sağ kenarı ise satır uzunluklarının ürettiği düzensiz bir profildir. Bu profile düzensiz sağ kenar (ragged right) denir ve düzensizliğinin derecesi ölçülebilir.

Aşağıdaki program bir paragrafı üç farklı satır uzunluğunda sarar, satır uzunluklarının dağılımını çıkarır ve iki yana yaslama (justification) uygulansaydı kelime aralıklarının ne kadar gerileceğini hesaplar. Ölçüm tek genişlikli varsayımla yapılır: her karakter 8 piksel, normal kelime aralığı 4 piksel kabul edilir.

// 04-rag.mjs — duzensiz sag kenarin derinligi ve iki yana yaslamanin bosluk maliyeti

const metin =
  "Katalog kaydinin ayrinti gorunumu kitabin baslik bilgisini, yazar adini, " +
  "basim yilini, raf kodunu ve o an rafta bulunup bulunmadigini tek ekranda " +
  "toplar. Okur bu ekrana iki soruyla gelir: aradigim kitap bu mu ve su anda " +
  "odunc alabilir miyim. Ayrinti gorunumunun duzeni bu iki soruyu ayri ayri " +
  "yanitlayacak bicimde kurulur.";

// Tek genislikli olcum: her karakter esit genislikte varsayilir.
function sar(metin, olcu) {
  const kelimeler = metin.split(" ");
  const satirlar = [];
  let simdiki = "";
  for (const k of kelimeler) {
    const aday = simdiki ? `${simdiki} ${k}` : k;
    if (aday.length <= olcu) simdiki = aday;
    else {
      satirlar.push(simdiki);
      simdiki = k;
    }
  }
  if (simdiki) satirlar.push(simdiki);
  return satirlar;
}

const KARAKTER_GENISLIGI = 8; // 16px govde metninde ortalama karakter genisligi
const NORMAL_BOSLUK = 4; // 0.25em kelime araligi

function olc(olcu) {
  const satirlar = sar(metin, olcu);
  const govde = satirlar.slice(0, -1); // son satir raga dahil edilmez
  const uzunluklar = govde.map((s) => s.length);
  const ort = uzunluklar.reduce((t, u) => t + u, 0) / uzunluklar.length;
  const sapma = Math.sqrt(
    uzunluklar.reduce((t, u) => t + (u - ort) ** 2, 0) / uzunluklar.length
  );
  const enKisa = Math.min(...uzunluklar);
  const ragDerinligi = olcu - enKisa;

  // Iki yana yaslama: eksik genislik kelime araliklarina dagitilir.
  const gerilmeler = govde.map((s) => {
    const bosluk = s.split(" ").length - 1;
    const eksik = (olcu - s.length) * KARAKTER_GENISLIGI;
    return bosluk > 0 ? eksik / bosluk : 0;
  });
  const enBuyukGerilme = Math.max(...gerilmeler);

  console.log(`olcu ${olcu} karakter  (${olcu * KARAKTER_GENISLIGI}px)`);
  console.log(`  satir sayisi: ${satirlar.length}  (rag hesabina giren: ${govde.length})`);
  console.log(`  satir uzunluklari: ${uzunluklar.join(", ")}`);
  console.log(`  ortalama ${ort.toFixed(1)}  standart sapma ${sapma.toFixed(2)}`);
  console.log(`  rag derinligi: ${ragDerinligi} karakter  (olcunun %${((100 * ragDerinligi) / olcu).toFixed(1)}'i)`);
  console.log(
    `  yaslamada en buyuk kelime araligi: ${(NORMAL_BOSLUK + enBuyukGerilme).toFixed(1)}px  ` +
      `(normalin ${((NORMAL_BOSLUK + enBuyukGerilme) / NORMAL_BOSLUK).toFixed(2)} kati)`
  );
  console.log("");
}

olc(45);
olc(68);
olc(92);
olcu 45 karakter  (360px)
  satir sayisi: 8  (rag hesabina giren: 7)
  satir uzunluklari: 41, 44, 32, 41, 43, 43, 42
  ortalama 40.9  standart sapma 3.76
  rag derinligi: 13 karakter  (olcunun %28.9'i)
  yaslamada en buyuk kelime araligi: 21.3px  (normalin 5.33 kati)

olcu 68 karakter  (544px)
  satir sayisi: 5  (rag hesabina giren: 4)
  satir uzunluklari: 65, 67, 68, 68
  ortalama 67.0  standart sapma 1.22
  rag derinligi: 3 karakter  (olcunun %4.4'i)
  yaslamada en buyuk kelime araligi: 7.4px  (normalin 1.86 kati)

olcu 92 karakter  (736px)
  satir sayisi: 4  (rag hesabina giren: 3)
  satir uzunluklari: 90, 89, 90
  ortalama 89.7  standart sapma 0.47
  rag derinligi: 3 karakter  (olcunun %3.3'i)
  yaslamada en buyuk kelime araligi: 5.7px  (normalin 1.43 kati)

Dar sütunun sağ kenarı derindir: bir satır 32 karakterde bitiyor, yani ölçünün yüzde 28.9’u boş kalıyor. Sağ kenar burada bir eksen değil, dişli bir profildir.

İki yana yaslama bu profili düzleştirmeyi vaat eder ama bedelini kelime aralıklarından öder. Dar sütunda en geniş satır aralığı normalin 5.33 katına çıkar. Kelime aralıkları harf aralıklarını aştığında satırlar arası boşluklar üst üste gelerek metnin içinde dikey şeritler oluşturur ve okuma satır boyunca değil, boşluklar arasında sıçrayarak ilerler.

Bu, bir tasarım kararını sayıya bağlar: iki yana yaslama, satır uzunluğu yeterince büyük olmadıkça uygulanmaz. Ölçüm 92 karakterlik sütunda gerilmeyi 1.43 katına indiriyor; 45 karakterlik sütunda 5.33 kata çıkarıyor. Katalog arayüzünün süzgeç panelindeki açıklama metinleri dar sütunlarda yaşar, dolayısıyla düzensiz sağ kenar korunur. Aynı hesabın diğer yönü de vardır: rag derinliği ölçünün dörtte birini aşıyorsa, sorun yaslama eksikliği değil sütunun fazla dar olmasıdır.

Özet

  • Hizalama, ögelerin paylaştığı görünmez bir eksendir; uzak ögeler arasında boşluk harcamadan ilişki kurar, karşılığında her yeni eksen okura bir kural daha yükler.
  • Eksen sayısı doğrudan sayılabilir bir düzen niteliğidir; ölçülen düzende yedi blok on üç eksenden dört eksene indirildi ve hiçbir bilgi kaybolmadı.
  • Alan ağırlıklı yatay merkezin kapsayıcı merkezinden sapması dengeyi ölçer; yarı genişliğin yüzde 10’unu aşan sapma gerekçe ister, ama sıfır sapma hedef değildir.
  • Optik hizalama yalnız kenarı düz olmayan biçimlerde gerekir, formülü yoktur ve uygulandığında kaydırma miktarı belgelenir.
  • Düzensiz sağ kenarın derinliği ölçülebilir; rag derinliği satır uzunluğunun dörtte birini aşıyorsa sütun fazla dardır.
  • İki yana yaslama, dar sütunda kelime aralıklarını normalin beş katına kadar gerer; kararı estetik değil bu gerilme oranı belirler.

Sonraki Adım

Bu ders tek bir ekranın iç tutarlılığını ölçtü. Asıl soru ekranlar arasındadır: katalog arayüzünün arama sayfasında 8 piksel olan bir boşluk, kayıt ayrıntısı sayfasında 10 piksel olduğunda ne kaybedilir? Sonraki ders yinelenmeyi öğrenilebilirliğin kaynağı olarak ele alır, bir arayüzün karar envanterini çıkarır ve aynı işi yapan kaç farklı değerin kullanıldığını sayar.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat