Ders 13 / 23
Dönüşümler
Öteleme, ölçekleme, döndürme ve eğme fonksiyonlarının matris karşılığı; fonksiyon sırasının sonucu değiştirmesi, dönüşüm kaynağının etkisi ve dönüşümün düzenden sonra uygulanması.
İçindekiler
Bir önceki ders kullanıcı tercihlerini okudu: renk şeması ve hareket azaltma. Renk şeması tercihinin neyi değiştirdiği bellidir — renk bildirimlerini. Hareket azaltma tercihi ise “yer değiştirme üreten” bildirimleri kaldırır ve o bildirimlerin ne olduğu henüz tanımlanmadı.
Bu konu o tanımı kurar. Sayfadaki hareket üç katmandan gelir: kutuyu kendi yerinde öteleyen, ölçekleyen ve döndüren geometrik işlemler, iki durum arasındaki değişimin zamana yayılması ve zaman çizelgesiyle sürülen anahtar kare dizileri. Bu ders birincisini ele alır. Üç işlemin ortak bir adı ve ortak bir matematiği vardır; ikisi de görüldüğünde hangi hareketin neyi hareket ettirdiği ölçülebilir bir soru hâline gelir.
Dönüşüm Düzenden Sonra Uygulanır
Dönüşüm (transform) bir ögenin kutusunu ötelemek, ölçeklemek, döndürmek veya eğmek için kullanılır. Belirleyici özelliği, düzen hesabına dokunmamasıdır.
Sıra şudur: tarayıcı düzeni hesaplar, kutunun yerini ve ölçüsünü bulur, sonra dönüşümü o
kutuya uygular. Ögenin düzendeki yeri değişmez; komşuları hiçbir şey olmamış gibi durur.
Bir ögeyi transform: translateX(40px) ile sağa itmek, margin-inline-start: 40px ile
itmekten bu yüzden farklıdır: ikincisi kapsayan bloğun içindeki bütün akışı yeniden
hesaplatır, birincisi yalnız o kutunun çizimini değiştirir.
Bunun üç sonucu vardır:
- Dönüşmüş öge komşularının üzerine taşabilir; taşma hesabı düzenden gelir, dönüşümden değil.
- Yüzde değerleri ögenin kendi kenarlık kutusuna göre okunur.
translateX(50%)ögenin kendi genişliğinin yarısıdır; oysaleft: 50%kapsayan bloğun genişliğinin yarısıdır. nonedışında bir değer taşıyan dönüşüm, yeni bir yığılma bağlamı kurar ve içindekifixedkonumlu torunlar için kapsayan blok olur. Bu kural CSS ile Görsel Sunum kursunun Yığılma Bağlamı dersinde tanımlanmıştı; hareket yazarken sık sık geri döner.
Üçüncü madde bir hata kaynağıdır. Sayfanın tamamını saran bir kapsayıcıya küçük bir dönüşüm yazmak, o kapsayıcının içindeki sabit konumlu bir başlığı ekrana değil kapsayıcıya sabitlenmiş duruma getirir.
Fonksiyonlar ve Matris Karşılıkları
Dönüşüm özelliği bir fonksiyon listesi alır. Listedeki her fonksiyon bir
matrise karşılık gelir ve liste bu matrislerin çarpımıdır. İki boyutta matrisin son satırı
her zaman olduğu için altı sayı yeterlidir; matrix() fonksiyonunun aldığı
sayılar bunlardır.
Bir nokta bu çarpımla yeni yerine taşınır. Aşağıdaki program fonksiyonları matrise çevirir, listeleri çarpar ve bir kartın dört köşesini izler.
// donusum.mjs — donusum fonksiyonlarinin matris karsiligi ve sira bagimliligi // matris [a, b, c, d, e, f] -> | a c e | // | b d f | // | 0 0 1 | const carp = (m, n) => [ m[0] * n[0] + m[2] * n[1], m[1] * n[0] + m[3] * n[1], m[0] * n[2] + m[2] * n[3], m[1] * n[2] + m[3] * n[3], m[0] * n[4] + m[2] * n[5] + m[4], m[1] * n[4] + m[3] * n[5] + m[5], ]; const rad = (d) => (d * Math.PI) / 180; const oteleme = (tx, ty) => [1, 0, 0, 1, tx, ty]; const olcekleme = (sx, sy) => [sx, 0, 0, sy, 0, 0]; const dondurme = (d) => [Math.cos(rad(d)), Math.sin(rad(d)), -Math.sin(rad(d)), Math.cos(rad(d)), 0, 0]; const egmeX = (d) => [1, 0, Math.tan(rad(d)), 1, 0, 0]; const egmeY = (d) => [1, Math.tan(rad(d)), 0, 1, 0, 0]; const BIRIM = [1, 0, 0, 1, 0, 0]; const zincir = (liste) => liste.reduce((t, m) => carp(t, m), BIRIM); const yaz = (m) => "matrix(" + m.map((v) => (Math.abs(v) < 1e-9 ? 0 : +v.toFixed(4))).join(", ") + ")"; const nokta = (m, [x, y]) => [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]]; const p = ([x, y]) => `(${x.toFixed(1)}, ${y.toFixed(1)})`; console.log("--- tek fonksiyonlarin matris karsiligi ---"); console.log("translate(40px, 12px) ->", yaz(oteleme(40, 12))); console.log("scale(1.25, 1.25) ->", yaz(olcekleme(1.25, 1.25))); console.log("rotate(30deg) ->", yaz(dondurme(30))); console.log("skewX(12deg) ->", yaz(egmeX(12))); console.log("skewY(12deg) ->", yaz(egmeY(12))); console.log("\n--- sira bagimliligi: ayni iki fonksiyon, iki yazim ---"); const A = zincir([oteleme(120, 0), dondurme(30)]); const B = zincir([dondurme(30), oteleme(120, 0)]); console.log("translate(120px, 0) rotate(30deg) ->", yaz(A)); console.log("rotate(30deg) translate(120px, 0) ->", yaz(B)); const KOSE = [[0, 0], [220, 0], [220, 120], [0, 120]]; // 220x120 birimlik kart console.log("\nkose noktalari (sol ust, sag ust, sag alt, sol alt):"); console.log(" yazim 1:", KOSE.map((k) => p(nokta(A, k))).join(" ")); console.log(" yazim 2:", KOSE.map((k) => p(nokta(B, k))).join(" ")); console.log("\n--- transform-origin: T(o) * M * T(-o) ---"); const ORIJIN = [[0, 0], [110, 60], [220, 120]]; // sol ust, merkez, sag alt for (const [ox, oy] of ORIJIN) { const M = zincir([oteleme(ox, oy), dondurme(30), oteleme(-ox, -oy)]); console.log(`origin (${ox}, ${oy})`.padEnd(18), yaz(M)); console.log(" koseler:", KOSE.map((k) => p(nokta(M, k))).join(" ")); } console.log("\n--- olcekleme ve otelemenin sirasi ---"); const C = zincir([olcekleme(2, 2), oteleme(50, 0)]); const D = zincir([oteleme(50, 0), olcekleme(2, 2)]); console.log("scale(2) translate(50px, 0) ->", yaz(C), " sol ust kose:", p(nokta(C, [0, 0]))); console.log("translate(50px, 0) scale(2) ->", yaz(D), " sol ust kose:", p(nokta(D, [0, 0]))); console.log("\n--- tek matrise indirgeme: uc fonksiyon, tek carpim ---"); const E = zincir([oteleme(0, -8), dondurme(-3), olcekleme(1.04, 1.04)]); console.log("translate(0, -8px) rotate(-3deg) scale(1.04) ->", yaz(E)); console.log("kart koseleri:", KOSE.map((k) => p(nokta(E, k))).join(" "));
--- tek fonksiyonlarin matris karsiligi --- translate(40px, 12px) -> matrix(1, 0, 0, 1, 40, 12) scale(1.25, 1.25) -> matrix(1.25, 0, 0, 1.25, 0, 0) rotate(30deg) -> matrix(0.866, 0.5, -0.5, 0.866, 0, 0) skewX(12deg) -> matrix(1, 0, 0.2126, 1, 0, 0) skewY(12deg) -> matrix(1, 0.2126, 0, 1, 0, 0) --- sira bagimliligi: ayni iki fonksiyon, iki yazim --- translate(120px, 0) rotate(30deg) -> matrix(0.866, 0.5, -0.5, 0.866, 120, 0) rotate(30deg) translate(120px, 0) -> matrix(0.866, 0.5, -0.5, 0.866, 103.923, 60) kose noktalari (sol ust, sag ust, sag alt, sol alt): yazim 1: (120.0, 0.0) (310.5, 110.0) (250.5, 213.9) (60.0, 103.9) yazim 2: (103.9, 60.0) (294.4, 170.0) (234.4, 273.9) (43.9, 163.9) --- transform-origin: T(o) * M * T(-o) --- origin (0, 0) matrix(0.866, 0.5, -0.5, 0.866, 0, 0) koseler: (0.0, 0.0) (190.5, 110.0) (130.5, 213.9) (-60.0, 103.9) origin (110, 60) matrix(0.866, 0.5, -0.5, 0.866, 44.7372, -46.9615) koseler: (44.7, -47.0) (235.3, 63.0) (175.3, 167.0) (-15.3, 57.0) origin (220, 120) matrix(0.866, 0.5, -0.5, 0.866, 89.4744, -93.923) koseler: (89.5, -93.9) (280.0, 16.1) (220.0, 120.0) (29.5, 10.0) --- olcekleme ve otelemenin sirasi --- scale(2) translate(50px, 0) -> matrix(2, 0, 0, 2, 100, 0) sol ust kose: (100.0, 0.0) translate(50px, 0) scale(2) -> matrix(2, 0, 0, 2, 50, 0) sol ust kose: (50.0, 0.0) --- tek matrise indirgeme: uc fonksiyon, tek carpim --- translate(0, -8px) rotate(-3deg) scale(1.04) -> matrix(1.0386, -0.0544, 0.0544, 1.0386, 0, -8) kart koseleri: (0.0, -8.0) (228.5, -20.0) (235.0, 104.7) (6.5, 116.6)
İlk blok dört ailenin matriste nereye yazıldığını gösteriyor. Öteleme (translate) yalnız
son iki sayıya, yani ve değerlerine dokunur; sol üstteki bölüm birim
matris kalır. Ölçekleme (scale) köşegene yazar. Döndürme (rotate) dört hücreyi de
kullanır ve değerler açının kosinüs ile sinüsüdür. Eğme (skew) köşegen dışı tek bir
hücreyi doldurur: skewX üstteki hücresini, skewY alttaki hücresini.
Eğmenin bir yan sonucu vardır: köşegen dışı bir hücre dolduğunda dik açılar korunmaz. Ölçekleme ve döndürme dikdörtgeni dikdörtgen bırakır, eğme paralelkenara çevirir.
Sıra Bağımlılığı
Matris çarpımı değişmeli değildir. Çıktının ikinci bloğu bunu iki yazımla gösteriyor. Aynı iki fonksiyon, farklı sırada yazıldığında iki farklı matris veriyor: birinde öteleme , diğerinde .
Kural şudur: liste soldan sağa çarpılır, noktaya ise sağdan sola uygulanır. Son yazılan fonksiyon noktaya ilk dokunandır.
translate(120px, 0) rotate(30deg)— kart önce kendi yerinde döner, sonra dönmüş hâliyle sayfanın eksenlerinde 120 birim sağa gider. Öteleme miktarı dönmeden etkilenmez.rotate(30deg) translate(120px, 0)— kart önce 120 birim ötelenir, sonra bu öteleme de dâhil her şey döner. Öteleme dönmüş eksende gerçekleşmiş gibi görünür: 120 birimlik hareket noktasına düşer, çünkü ve .
Ölçekleme ile öteleme çiftinde aynı olay daha çıplaktır. scale(2) translate(50px, 0)
yazımında öteleme de ikiye katlanır ve kart 100 birim gider; ters yazımda 50 birim gider.
Ölçeklemeden sonra yazılan uzunluklar ölçeklenmiş birimlerdedir.
Bu, hareket yazarken en sık kırılan kuraldır. Bir kartın hem büyüyüp hem yukarı kalkması isteniyorsa iki fonksiyonun sırası, hareketin yönünü ve miktarını belirler.
Dönüşüm Kaynağı
Döndürme ve ölçekleme sabit bir noktayı yerinde bırakır. Bu noktaya dönüşüm kaynağı (transform-origin) denir ve varsayılan değeri kutunun merkezidir.
Matris karşılığı ayrı bir mekanizma değildir: kaynak, dönüşümü üç adımlık bir zincire çevirir. Kaynak noktası başlangıca taşınır, dönüşüm uygulanır, nokta yerine geri götürülür. Çıktının üçüncü bloğu üç kaynak için bu zinciri hesaplıyor.
Sonuçların okunuşu şöyledir: kaynak sol üst köşedeyken noktası yerinde kalıyor; kaynak sağ alt köşedeyken noktası yerinde kalıyor. Aradaki fark ve değerlerine yansıyor — döndürme matrisinin ilk dört sayısı üç durumda da aynı.
transform-origin iki ya da üç değer alır; yüzdeler ögenin kendi kenarlık kutusuna göre
okunur. Kart açılırken sol kenarından açılsın isteniyorsa kaynak left center yazılır.
Üç Boyuta Geçiş
İki boyutlu listeye translateZ, rotateX, rotateY gibi bir fonksiyon girdiğinde matris
olur ve matrix3d() on altı sayı alır. Kural aynı kalır: liste çarpılır,
noktaya sağdan sola uygulanır.
Üç boyutta iki ek bildirim devreye girer. perspective bir bakış uzaklığı tanımlar; küçük
değer güçlü, büyük değer zayıf bir derinlik etkisi verir. Bu değer ögenin kendisine
transform listesinde ya da ana ögesine perspective özelliğiyle yazılabilir; ikincisi
kardeş ögelerin ortak bir bakış noktasını paylaşmasını sağlar.
transform-style: preserve-3d ise alt ögelerin düzleştirilmeden kendi derinliklerini
korumasını söyler. Varsayılan flat değeri, alt ağacı ana ögenin düzlemine bastırır.
backface-visibility: hidden bildirimi, ögenin arka yüzü izleyiciye döndüğünde
çizilmemesini bildirir — iki yüzü olan bir kartın ters yüzünü gizlemek için kullanılır.
Üç boyutlu dönüşümlerin bir bedeli vardır ve bu konunun dördüncü dersinde ele alınır: derinlik taşıyan bir öge, çizimde kendi katmanına ayrılır.
İstasyon Sayfasında Dönüşüm
Ölçüm kartlarına dokunma göstergesi ekleyelim. Kart, işaretçi üzerine geldiğinde bir miktar yükselsin ve çok az büyüsün.
/* hareket.css — 1. adım: kart vurgusu */ .olcum-kartlari > .kart { transform-origin: center bottom; } .olcum-kartlari > .kart:hover, .olcum-kartlari > .kart:focus-within { transform: translateY(-8px) scale(1.04); }
Sıra bilinçlidir. translateY(-8px) önce yazıldığı için 8 birimlik yükselme
ölçeklenmemiştir; ters sırada yazılsaydı yükselme birim olurdu.
Fark küçüktür, ama aynı kural 200 birimlik bir öteleme için sekiz birimlik bir sapma üretir.
transform-origin: center bottom kartın alt kenarını sabitler: kart kendi tabanından
büyür, komşu kartın üzerine simetrik olarak taşmaz.
:focus-within sözde sınıfı, :hover ile aynı bildirimi klavye kullanıcısına da verir.
Kart içindeki bir bağlantı odak aldığında kart aynı biçimde yükselir.
Bu dosya duzen.css dosyasının yanına konur. Üçüncü bir dosyanın gerekip gerekmediği —
görsel, düzen ve hareket bildirimlerinin nasıl bölüneceği — kursun son konusunun sorusudur.
Özet
- Dönüşüm düzen hesabından sonra uygulanır; ögenin düzendeki yeri ve komşularının konumu değişmez, yalnızca çizilen kutu taşınır.
- Dönüşüm listesindeki her fonksiyon bir matristir; liste bu matrislerin çarpımıdır ve
matrix()yazımı iki boyutlu bir dönüşümün altı sayısını verir. - Çarpım değişmeli olmadığı için fonksiyon sırası sonucu değiştirir: liste soldan sağa çarpılır, noktaya sağdan sola uygulanır ve ölçeklemeden sonra yazılan uzunluklar ölçeklenmiş birimlerdedir.
transform-originayrı bir mekanizma değil, dönüşümün önüne ve arkasına eklenen bir öteleme çiftidir; varsayılanı kutunun merkezidir.nonedışında bir dönüşüm değeri yığılma bağlamı kurar ve sabit konumlu torunlar için kapsayan blok olur.
Sonraki Adım
Bu dersin bildirimleri hareketi anlık yapar: işaretçi kartın üzerine geldiği anda kart sekiz birim yukarıda ve yüzde dört büyüktür, arada bir şey yoktur. Oysa hareket dediğimiz şey iki durum arasındaki yoldur. Sonraki ders bu yolu tanımlayan bildirimleri ele alır: hangi özellik ne kadar sürede, hangi hız eğrisiyle eski değerinden yenisine gider?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.