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 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 ile arasında zaman, çıktı ile arasında ilerlemedir.
Adlı fonksiyonların tamamı, ve 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 parametresine göre tanımlıdır, oysa elimizdeki zaman eksenindedir. Önce değerini veren bulunur, sonra o için 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 ; öteleme bu oranda birim, ölçek çarpanı .
Formül her özellik için aynıdır:
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 birimdir ve geri dönüş bu değerden başlar, 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 , , ,
, 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,displaygibi 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
lineardışı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;startilk basamağı hemen atar,endson 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.