Transform & Composite — Web Animation

Transform & Composite CSS Transform adalah properti paling penting untuk animasi karena perubahan transform tidak memicu layout ulang (reflow). Transform…

transform menggeser, memutar, atau mengubah ukuran gambar sebuah elemen tanpa mengubah tempatnya di layout. Bayangin foto kamar yang ditempel di plastik bening di atas halaman. Menggeser plastiknya gak membuat teks lain pindah. Tapi kalau fotonya dipindah di dalam dokumen, semua teks di bawahnya mengalir ulang. top dan margin memindah foto di dokumen; transform cuma menggeser plastiknya. Karena itu transform dan opacity bisa dianimasikan di langkah composite saja, tanpa layout dan paint ulang.

🧪 Tebakan cepat: Kartu Kamar Teras naik 16 px dalam 300 ms. Kalau digerakkan lewat top, Chromium menghitung layout berapa kali? Kalau lewat transform? Jawabannya ada di bagian "Diukur: transform lawan top".

Fungsi transform

Fungsi Artinya Di halaman Bu Sri
translate(x, y), translateX(), translateY() geser; persen dihitung dari ukuran elemen itu sendiri kartu naik translateY(16px), galeri translateX(-100%)
scale(n) perbesar atau perkecil tombol malam ditekan scale(0.96)
rotate(sudut) putar panah galeri rotate(180deg)
skew(sudut) miringkan jarang dipakai di UI

Titik pusatnya diatur transform-origin, default di tengah elemen (50% 50%). scale(0.96) di tombol malam mengecil ke arah tengahnya; bar hitung mundur di toast memakai transform-origin: left supaya menyusut ke kiri.

Urutan fungsi menentukan hasil

transform boleh berisi beberapa fungsi, dan urutannya mengubah hasil. Baca dari kanan ke kiri: fungsi paling kanan dikenakan duluan ke elemen, lalu hasilnya dikenai fungsi di sebelah kirinya. Kami memasang keduanya pada kotak 100 × 40 px di Chromium 147, WebKit 26.4, dan Firefox 146:

transform Yang terjadi Kotak pembatasnya
translateX(50px) rotate(45deg) diputar di tempat, lalu digeser lurus 50 px ke kanan kiri 150,5, atas 70,5
rotate(45deg) translateX(50px) digeser 50 px dulu, lalu seluruhnya diputar 45° di sekitar titik pusat, jadi geserannya ikut miring: 35,4 px ke kanan dan 35,4 px ke bawah kiri 135,9, atas 105,9

Jadi ya, urutan transform mempengaruhi hasil akhir.

translate, rotate, scale sebagai properti sendiri

Sejak Chrome 104, Firefox 72, dan Safari 14.1, ketiganya juga ada sebagai properti terpisah: translate, rotate, scale. Urutannya tetap: translate, lalu rotate, lalu scale, lalu transform, dibaca dari kiri seperti satu transform panjang. Kami mengukurnya: rotate: 45deg; transform: translateX(50px) sama persis dengan transform: rotate(45deg) translateX(50px). Dan scale: 2; transform: translateX(50px) menggeser kotaknya 100 px, karena geseran di transform ikut diperbesar scale.

Gunanya: tiap gerakan punya properti sendiri, jadi gak saling menimpa.

.room { translate: 0 16px; opacity: 0;
        transition: translate 300ms ease-out, opacity 300ms ease-out, scale 150ms ease-out; }
.room.in { translate: none; opacity: 1; }        /* masuk */
.room.selected { scale: 1.02; }                  /* dipilih, gak menghapus geser masuknya */

Kalau keduanya ditulis lewat transform, transform: scale(1.02) milik kartu terpilih menggantikan seluruh transform: translateY(16px) milik animasi masuk.

Diukur: transform lawan top

Kami merekam Performance trace di Chromium 147 saat kartu Kamar Teras naik 16 px dalam 300 ms, dengan Kamar Loteng dan Kamar Kebun di bawahnya. Angkanya dari tiga kali rekam:

Kartu naik lewat Layout Paint
top: 16px jadi 0 35 sampai 37 kali 70 sampai 72 kali
margin-top: 16px jadi 0 37 sampai 39 kali 74 sampai 76 kali, dua kartu di bawahnya ikut bergeser
transform: translateY(16px) jadi none 1 kali 2 kali, di awal saja
translate: 0 16px jadi none 1 kali 2 kali, di awal saja
transform + will-change: transform 0 0

Itu jawaban tebakannya: lewat top, layout dihitung 35 sampai 37 kali (hampir di setiap frame), lewat transform cuma 1 kali. Satu layout dan dua paint di awal itu terjadi saat browser memindahkan kartu ke layer-nya sendiri.

Properti yang cukup sampai composite

Composite menyusun layer: gambar-gambar yang sudah di-paint. Kalau kartu punya layer sendiri, menggeser, memutar, mengubah ukuran (transform), atau mengubah transparansinya (opacity) cuma berarti menyusun ulang layer itu dengan posisi atau transparansi baru. Gak ada piksel yang perlu di-paint ulang. Pekerjaan itu bisa dilakukan compositor thread tanpa menunggu main thread.

Properti lain gak seberuntung itu. Di trace yang sama, box-shadow yang menebal: 0 layout tapi 74 paint, karena bayangannya harus dilukis ulang tiap frame. filter: blur(4px) jadi blur(0): 0 layout dan 6 paint, tapi style dihitung ulang 37 sampai 39 kali (transform: 7 sampai 9 kali), jadi main thread tetap ikut bekerja di setiap frame. Blur juga mahal untuk GPU: tiap piksel dihitung dari banyak piksel tetangganya.

will-change: layer disiapkan sebelum mulai

will-change: transform memberi tahu browser bahwa transform elemen ini akan berubah. Browser langsung membuatkan layer sendiri, sebelum animasinya mulai. Hasilnya di trace: 0 layout dan 0 paint, dibanding 1 layout dan 2 paint di awal tanpa will-change. Frame pertama gak perlu menunggu layer dibuat.

Harganya memori. Tiap layer adalah gambar bitmap di memori GPU: lebar × tinggi × 4 byte per piksel. Kartu Kamar Teras berukuran 328 × 143 px; di HP dengan layar 3x, layer-nya 984 × 429 piksel × 4 byte = sekitar 1,7 MB. Dari data layer Chromium (LayerTree, yang juga dipakai panel Layers), 3 kartu dengan will-change menambah 3 layer, dan 30 kartu menambah 30 layer, sekitar 51 MB. will-change juga membuat stacking context baru, seperti transform (ingat pelajaran Z-Index).

card.style.willChange = 'transform';        // tepat sebelum animasi
card.classList.add('in');
card.addEventListener('transitionend', () => {
  card.style.willChange = '';               // lepas lagi setelah selesai
}, { once: true });

Pasang di elemen yang memang akan bergerak, sebentar sebelum bergerak, dan lepas setelahnya. Jangan di semua elemen.

⚠️ Jebakan yang sering ketemu

TL;DR: transform (translate, scale, rotate, skew) mengubah gambar elemen tanpa mengubah layout; persen di translate dihitung dari ukuran elemen sendiri, titik pusatnya transform-origin. Urutan fungsi mempengaruhi hasil: dibaca dari kanan ke kiri, jadi translateX(50px) rotate(45deg) bergeser lurus sedangkan rotate(45deg) translateX(50px) bergeser miring. Properti translate, rotate, scale (Chrome 104, Firefox 72, Safari 14.1) dikenakan dengan urutan translate, rotate, scale, lalu transform, dan gak saling menimpa. Di Chromium 147, kartu naik 16 px dalam 300 ms lewat top = 35 sampai 37 layout dan 70 sampai 72 paint; lewat transform = 1 layout dan 2 paint di awal; dengan will-change: transform = 0 dan 0. transform dan opacity cukup sampai composite; box-shadow di-paint ulang tiap frame, filter tetap menghitung style tiap frame. will-change menyiapkan layer sebelum animasi mulai, tapi tiap layer memakan memori (kartu 328 × 143 px di layar 3x sekitar 1,7 MB): pasang sebentar, lalu lepas.

Yang akan kamu pelajari