---
title: Dönüşümler
source: 'https://academia.sh/tr/kurslar/duzen-ve-duyarli-tasarim/donusumler'
course: 'Düzen Sistemleri ve Duyarlı Tasarım'
language: tr
updated: '2026-08-17T18:09:27+00:00'
license: 'CC BY-SA 4.0'
---

# 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ı.

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:

1. Dönüşmüş öge komşularının üzerine taşabilir; taşma hesabı düzenden gelir, dönüşümden
   değil.
2. Yüzde değerleri **ögenin kendi kenarlık kutusuna** göre okunur. `translateX(50%)` ögenin
   kendi genişliğinin yarısıdır; oysa `left: 50%` kapsayan bloğun genişliğinin yarısıdır.
3. `none` dışında bir değer taşıyan dönüşüm, yeni bir **yığılma bağlamı** kurar ve içindeki
   `fixed` konumlu 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 $3 \times 3$
matrise karşılık gelir ve liste bu matrislerin çarpımıdır. İki boyutta matrisin son satırı
her zaman $(0, 0, 1)$ olduğu için altı sayı yeterlidir; `matrix()` fonksiyonunun aldığı
sayılar bunlardır.

$$
\begin{pmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{pmatrix}
\begin{pmatrix} x \\ y \\ 1 \end{pmatrix}
=
\begin{pmatrix} ax + cy + e \\ bx + dy + f \end{pmatrix}
$$

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.

```js
// 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 $e$ ve $f$ değerlerine dokunur; sol üstteki $2 \times 2$ 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 $c$ hücresini, `skewY` alttaki $b$ 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
$(120, 0)$, diğerinde $(103{,}9;\ 60)$.

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 $(103{,}9;\ 60)$ noktasına düşer, çünkü $120 \cos 30° \approx 103{,}9$ ve
  $120 \sin 30° = 60$.

Ö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 $(0, 0)$ noktası yerinde kalıyor;
kaynak sağ alt köşedeyken $(220, 120)$ noktası yerinde kalıyor. Aradaki fark $e$ ve $f$
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
$4 \times 4$ 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.

```css
/* 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 $8 \times 1{,}04 = 8{,}32$ 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-origin` ayrı bir mekanizma değil, dönüşümün önüne ve arkasına eklenen bir
  öteleme çiftidir; varsayılanı kutunun merkezidir.
- `none` dışı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 **yol**dur. 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?
