İçeriğe geç
academia.sh

Ders 14 / 23

Geçişler

İki durum arasındaki değişimin zamana yayılması; geçebilen ve geçemeyen değerler, kübik Bézier zamanlama eğrisinin hesabı, kesilen geçişin davranışı ve kesikli zamanlama.

İçindekiler

Bir önceki ders kartın vurgulu durumunu tanımladı: sekiz birim yukarıda, yüzde dört büyük. Bildirim iki durumu da veriyor ama aralarında bir yol yok. İşaretçi kartın üzerine geldiği anda kart yeni yerindedir.

Hareket, iki durum arasındaki yolun zamana yayılmasıdır. Bu ders o yolu tanımlayan bildirimleri ele alır: hangi özellik ne kadar sürede, hangi hız eğrisiyle eski değerinden yenisine gider ve yol yarıda kesilirse ne olur?

Geçiş İki Değer Arasını Doldurur

Geçiş (transition), bir özelliğin hesaplanmış değeri değiştiğinde eski değerden yenisine zaman içinde ilerlemesini sağlar. Değişimi geçiş başlatmaz; geçiş yalnız zaten olacak bir değişimi yayar.

Bir uyarı gerekir: CSS ile Görsel Sunum kursunda “geçiş” sözcüğü renkler arasındaki hesaplanmış görsel için kullanılmıştı. Bu kursta o anlam renk geçişi olarak anılır; tek başına “geçiş”, bu dersteki zaman içinde değişim anlamındadır.

Geçişin dört ayrı bildirimi vardır:

Bildirim Yanıtladığı soru
transition-property Hangi özellikler yayılacak
transition-duration Yol ne kadar sürecek
transition-timing-function Yol boyunca hız nasıl dağılacak
transition-delay Değişimden ne kadar sonra başlayacak

Kısayol transition, bu dördünü tek satırda alır ve virgülle ayrılmış listeler her özellik için ayrı değer yazmayı sağlar. Süre yazılmazsa varsayılan sıfırdır; süresiz bir geçiş bildirimi hiçbir şey yapmaz.

transition-delay eksi değer de alabilir. Eksi gecikme, geçişi başlangıcından değil ortasından başlatır: 240 milisaniyelik bir geçişe 60-60 milisaniye gecikme yazmak, geçişi yüzde 25 ilerlemiş durumda açar.

Geçebilen ve Geçemeyen Değerler

Geçiş, iki değer arasında ara değer üretebilmeyi gerektirir. Uzunluk, sayı, renk ve dönüşüm listesi için bu tanımlıdır. Anahtar sözcükler için tanımlı değildir.

Bunun sonuçları vardır. height: auto değerinden sabit bir yüksekliğe geçiş çalışmaz, çünkü auto bir sayı değildir. display: none ile görünür durum arasında ara değer yoktur; görüntüleme türü kesikli (discrete) bir özelliktir ve kesikli özellikler geçişin ortasında değil sonunda ya da başında değişir.

Bir açılır bölümün yüksekliğinin yayılması isteniyorsa üç yol vardır: sabit bir üst sınır üzerinden geçmek, ızgara şeridinin pay değerini geçirmek ya da yüksekliği hiç oynatmadan dönüşüm ve saydamlıkla çalışmak. Üçüncüsü, sonraki derslerin göstereceği nedenle en ucuzudur.

visibility özelliği bir istisnadır: kesikli olduğu hâlde geçiş süresince eski değerini korur, bu yüzden opacity ile birlikte yazıldığında ögenin sönerken tıklanabilir kalmasını önler.

Zamanlama Eğrisi

Zamanlama fonksiyonu (timing function), geçen sürenin oranını ilerleme oranına çevirir. Girdi 00 ile 11 arasında zaman, çıktı 00 ile 11 arasında ilerlemedir.

Adlı fonksiyonların tamamı, (0,0)(0,0) ve (1,1)(1,1) uçlarını iki denetim noktasıyla birleştiren kübik Bézier eğrisidir. cubic-bezier(x1, y1, x2, y2) yazımı bu iki noktayı verir.

Eğrinin değerlendirilmesi tek adımda yapılamaz: eğri tt parametresine göre tanımlıdır, oysa elimizdeki zaman xx eksenindedir. Önce xx değerini veren tt bulunur, sonra o tt için yy okunur.

// gecis.mjs — kubik Bezier zamanlama egrisinin degerlendirilmesi ve ara deger hesabi
// Zamanlama egrisi (0,0) ile (1,1) arasinda, denetim noktalari (x1,y1) ve (x2,y2).
const bez = (a, b, t) => {
  const u = 1 - t;
  return 3 * u * u * t * a + 3 * u * t * t * b + t * t * t;   // p0=0, p3=1
};

// Verilen x icin egri parametresi t ikili aramayla bulunur, sonra y okunur.
function egri(x1, y1, x2, y2) {
  return (x) => {
    if (x <= 0) return 0;
    if (x >= 1) return 1;
    let alt = 0, ust = 1, t = x;
    for (let i = 0; i < 60; i++) {
      t = (alt + ust) / 2;
      if (bez(x1, x2, t) < x) alt = t; else ust = t;
    }
    return bez(y1, y2, t);
  };
}

const ADLI = {
  linear: [0, 0, 1, 1],
  ease: [0.25, 0.1, 0.25, 1],
  "ease-in": [0.42, 0, 1, 1],
  "ease-out": [0, 0, 0.58, 1],
  "ease-in-out": [0.42, 0, 0.58, 1],
};

console.log("--- adli zamanlama fonksiyonlarinin ilerleme degerleri ---");
console.log("gecen sure".padEnd(12) + Object.keys(ADLI).map((k) => k.padStart(12)).join(""));
for (let i = 0; i <= 10; i++) {
  const x = i / 10;
  const satir = Object.values(ADLI).map((c) => egri(...c)(x).toFixed(4).padStart(12)).join("");
  console.log(`%${(x * 100).toFixed(0)}`.padEnd(12) + satir);
}

console.log("\n--- ayni ilerlemenin iki ozellige uygulanmasi (240 ms, ease-out) ---");
const f = egri(...ADLI["ease-out"]);
const SURE = 240;
const ara = (bas, son, p) => bas + (son - bas) * p;
console.log("ms".padStart(5), "ilerleme".padStart(10), "translateY".padStart(12), "scale".padStart(10));
for (const ms of [0, 30, 60, 90, 120, 180, 240]) {
  const p = f(ms / SURE);
  console.log(
    String(ms).padStart(5),
    p.toFixed(4).padStart(10),
    (ara(0, -8, p).toFixed(2) + "px").padStart(12),
    ara(1, 1.04, p).toFixed(4).padStart(10),
  );
}

console.log("\n--- gecisin ortasinda kesilmesi ---");
const KESME = 72;                     // ms; isaretci 72. ms'de karttan cikiyor
const durum = ara(0, -8, f(KESME / SURE));
console.log(`kesme aninda translateY = ${durum.toFixed(2)}px`);
console.log("geri donus, ayni sure ve ayni egriyle bu degerden 0'a gider:");
for (const ms of [0, 30, 60, 90, 120, 180, 240]) {
  const p = f(ms / SURE);
  console.log(`  ${String(ms).padStart(3)} ms -> ${ara(durum, 0, p).toFixed(2)}px`);
}

console.log("\n--- steps() ile kesikli ilerleme, 4 adim ---");
const basamak = (n, kip) => (x) => {
  if (x <= 0) return kip === "start" ? 1 / n : 0;
  if (x >= 1) return 1;
  const k = Math.floor(x * n);
  return (kip === "start" ? k + 1 : k) / n;
};
const bStart = basamak(4, "start");
const bEnd = basamak(4, "end");
console.log("gecen sure".padEnd(12) + "steps(4, start)".padStart(16) + "steps(4, end)".padStart(16));
for (let i = 0; i <= 10; i++) {
  const x = i / 10;
  console.log(
    `%${(x * 100).toFixed(0)}`.padEnd(12) +
    bStart(x).toFixed(2).padStart(16) +
    bEnd(x).toFixed(2).padStart(16),
  );
}
--- adli zamanlama fonksiyonlarinin ilerleme degerleri ---
gecen sure        linear        ease     ease-in    ease-out ease-in-out
%0                0.0000      0.0000      0.0000      0.0000      0.0000
%10               0.1000      0.0948      0.0170      0.1606      0.0197
%20               0.2000      0.2952      0.0623      0.3084      0.0817
%30               0.3000      0.5133      0.1296      0.4452      0.1874
%40               0.4000      0.6825      0.2149      0.5709      0.3319
%50               0.5000      0.8024      0.3154      0.6846      0.5000
%60               0.6000      0.8852      0.4291      0.7851      0.6681
%70               0.7000      0.9408      0.5548      0.8704      0.8126
%80               0.8000      0.9756      0.6916      0.9377      0.9183
%90               0.9000      0.9943      0.8394      0.9830      0.9803
%100              1.0000      1.0000      1.0000      1.0000      1.0000

--- ayni ilerlemenin iki ozellige uygulanmasi (240 ms, ease-out) ---
   ms   ilerleme   translateY      scale
    0     0.0000       0.00px     1.0000
   30     0.1986      -1.59px     1.0079
   60     0.3781      -3.03px     1.0151
   90     0.5405      -4.32px     1.0216
  120     0.6846      -5.48px     1.0274
  180     0.9065      -7.25px     1.0363
  240     1.0000      -8.00px     1.0400

--- gecisin ortasinda kesilmesi ---
kesme aninda translateY = -3.56px
geri donus, ayni sure ve ayni egriyle bu degerden 0'a gider:
    0 ms -> -3.56px
   30 ms -> -2.85px
   60 ms -> -2.21px
   90 ms -> -1.64px
  120 ms -> -1.12px
  180 ms -> -0.33px
  240 ms -> 0.00px

--- steps() ile kesikli ilerleme, 4 adim ---
gecen sure   steps(4, start)   steps(4, end)
%0                      0.25            0.00
%10                     0.25            0.00
%20                     0.25            0.00
%30                     0.50            0.25
%40                     0.50            0.25
%50                     0.75            0.50
%60                     0.75            0.50
%70                     0.75            0.50
%80                     1.00            0.75
%90                     1.00            0.75
%100                    1.00            1.00

İlk tablo beş eğriyi aynı zaman noktalarında karşılaştırıyor. linear dışındaki hepsi zamanın yarısında yolun yarısında değil.

ease yolun yarısını sürenin yaklaşık yüzde 30’unda tamamlar; hızlı başlar, uzun bir yavaşlama kuyruğu bırakır. ease-in yavaş başlar, sona doğru hızlanır — sürenin yarısında yolun ancak üçte biri alınmıştır. ease-out bunun tersidir. ease-in-out simetriktir ve tam ortada yolun tam yarısındadır.

Seçim keyfî değildir. Bir öge ekrana giriyorsa yavaşlayarak durması beklenir (ease-out); ekrandan çıkıyorsa hızlanarak gitmesi beklenir (ease-in). Yerinde kalıp biçim değiştiren bir ögede simetrik eğri kullanılır.

Ara Değerin Hesabı

Çıktının ikinci bloğu, tek bir ilerleme değerinin iki ayrı özelliğe nasıl uygulandığını gösteriyor. 240 milisaniyelik geçişin 60. milisaniyesinde ilerleme 0,37810{,}3781; öteleme bu oranda 3,03-3{,}03 birim, ölçek çarpanı 1,01511{,}0151.

Formül her özellik için aynıdır:

deg˘er(t)=bas¸langıc¸+(bitis¸bas¸langıc¸)×ilerleme(t)\text{değer}(t) = \text{başlangıç} + (\text{bitiş} - \text{başlangıç}) \times \text{ilerleme}(t)

Dönüşüm listelerinde bu hesap fonksiyon fonksiyon yapılır — iki listede aynı fonksiyonlar aynı sırada varsa her fonksiyonun kendi sayıları ara değerlenir. Listeler uyuşmuyorsa tarayıcı ikisini de matrise indirger ve matrisleri ara değerler; sonuç genellikle beklenen yol değildir. Geçişe girecek iki dönüşüm listesinin aynı fonksiyonları aynı sırada taşıması bu yüzden bir yazım kuralıdır.

Kesilme ve Geri Dönüş

Çıktının üçüncü bloğu, işaretçinin 72. milisaniyede karttan çıktığı durumu hesaplıyor. O anda öteleme 3,56-3{,}56 birimdir ve geri dönüş bu değerden başlar, 8-8 biriminden değil.

Kural şudur: bir geçiş kesildiğinde yeni geçiş, özelliğin o andaki hesaplanmış değerinden başlar. Bu, hareketin sıçramamasını sağlar.

Ama bir yan etki bırakır: geri dönüş de tam süreyi kullanır. Kart 72 milisaniye yükselip 240 milisaniye boyunca iner. Kısa bir dokunuş, uzun bir geri dönüşle yanıtlanır ve hareket gecikmeli hissettirir. Bunun önüne geçmek için giriş ve çıkış süreleri ayrı yazılır: çıkış süresi girişten kısa tutulur.

Bir geçiş bildirimi, ögenin temel durumuna yazıldığında hem gidiş hem dönüş için geçerlidir; yalnız :hover kuralına yazıldığında yalnız gidiş yayılır, dönüş anlık olur.

Kesikli Zamanlama

steps(n, kip) fonksiyonu ilerlemeyi sürekli değil basamaklı üretir. Çıktının son bloğu dört adımlık iki kipi karşılaştırıyor.

end kipi sıfırdan başlar ve son basamağı sonda atar; start kipi ilk basamağı hemen atar ve sıfır değerini hiç göstermez. Dört adımda ilerleme yalnız 00, 0,250{,}25, 0,50{,}5, 0,750{,}75, 11 değerlerini alır; aradaki her şey atlanır.

Bu, kare kare ilerleyen bir gösterge ya da sayaç için kullanılır. Süreklilik istenmeyen yerlerde kesikli zamanlama, hareketi daha az dikkat dağıtıcı yapar.

İstasyon Sayfasında Geçiş

/* hareket.css — 2. adım: kart vurgusunun zamana yayılması */
.olcum-kartlari > .kart {
  transform-origin: center bottom;
  transition:
    transform 240ms ease-out,
    box-shadow 240ms ease-out;
}

.olcum-kartlari > .kart:hover,
.olcum-kartlari > .kart:focus-within {
  transform: translateY(-8px) scale(1.04);
  transition-duration: 160ms;
}

Geçiş bildirimi temel duruma yazıldı; dönüş de yayılıyor. Vurgulu durumda süre 160 milisaniyeye çekildi: giriş kısa, dönüş biraz uzun. Sıralama böylece hem tepkili hem yumuşak olur.

Listede transform ve box-shadow tek tek sayıldı. transition-property: all yazmak daha az yazıdır ama her hesaplanmış değer değişimini geçişe sokar; sonradan eklenen bir renk ya da ölçü bildirimi istemeden yayılmaya başlar. Özellikleri saymak bu sürprizi önler.

Özet

  • Geçiş bir değişimi başlatmaz; hesaplanmış değeri zaten değişen bir özelliğin eski değerinden yenisine ilerlemesini zamana yayar.
  • Ara değer üretilemeyen değerler geçemez: auto, display gibi kesikli özellikler ve anahtar sözcükler yol değil sıçrama üretir.
  • Zamanlama fonksiyonu geçen sürenin oranını ilerleme oranına çevirir; adlı fonksiyonlar kübik Bézier eğrileridir ve linear dışında zamanın yarısı yolun yarısı değildir.
  • Kesilen bir geçiş, özelliğin o andaki hesaplanmış değerinden yeniden başlar ve tam süresini kullanır; giriş ile çıkış süreleri ayrı yazılarak dengelenir.
  • steps(n, kip) ilerlemeyi basamaklı üretir; start ilk basamağı hemen atar, end son basamağı sonda atar.

Sonraki Adım

Geçiş bir koşula bağlıdır: değer değişmelidir. Değer kendiliğinden değişmiyorsa — bir yükleme göstergesi dönmeye devam ediyorsa, bir uyarı iki renk arasında gidip geliyorsa — geçişin tetikleyecek bir olayı yoktur. Sonraki ders, zaman çizelgesini bildirimin kendisine yazan mekanizmayı ele alır: ara duraklar, yineleme sayısı ve yön.

İ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