Animasi terasa mulus kalau browser bisa menggambar 60 frame tiap detik. Artinya tiap frame harus selesai dalam 16,7 ms (1.000 ms dibagi 60). Kalau satu frame lebih lama, frame itu telat tampil, dan gerakannya patah-patah. Patah-patah ini disebut jank. Bayangin bikin animasi flipbook, buku kecil yang halamannya dibalik cepat. Kalau di tiap halaman kamu menggambar ulang seluruh adegan dari nol, gak keburu. Kalau tokohnya digambar sekali di plastik transparan, lalu di tiap halaman plastiknya cuma digeser, keburu. Menggambar ulang itu left, menggeser plastik itu transform.
🧪 Tebakan cepat: di halaman contoh (1.500 elemen), menggeser kotak pakai
leftmakan 17,0 ms per frame. Kalau digantitransform: translateX(), jadi berapa: 15 ms, 8 ms, atau sekitar 1 ms? Jawabannya ada di bagian "left atau transform, frame demi frame".
60 frame per detik
Layar biasa menampilkan gambar baru 60 kali per detik. Di sela itu browser punya waktu 16,7 ms untuk menyiapkan frame berikutnya: menjalankan Style, Layout, Paint, dan Composite yang kamu kenal dari pelajaran CSS & Rendering Pipeline.
- Frame selesai di bawah 16,7 ms: tampil tepat waktu, gerakan mulus.
- Frame butuh 17 ms: ketinggalan satu kali giliran layar, jadi baru tampil di giliran berikutnya. Kalau tiap frame begitu, animasinya cuma jalan 30 frame per detik (30 fps).
- Layar 120 Hz punya giliran lebih rapat: cuma 8,3 ms per frame.
left atau transform, frame demi frame
Lab menggeser kotak bolak-balik di halaman yang punya 1.500 elemen. Ini biaya satu frame (angka simulasi untuk HP kelas menengah):
| Tahap | left |
transform: translateX() |
|---|---|---|
| Style | 0,5 ms | 0,5 ms |
| Layout | 12,0 ms | dilewati |
| Paint | 4,0 ms | dilewati |
| Composite | 0,5 ms | 0,5 ms |
| Total | 17,0 ms, lewat 16,7 ms, jadi 30 fps | 1,0 ms, 60 fps |
Itu jawaban tebakannya: sekitar 1 ms. left mengubah posisi di layout, jadi browser menghitung ulang Layout seluruh halaman dan mengecat ulang area yang berubah di tiap frame. transform gak menyentuh layout: kotak digambar sekali ke lapisannya sendiri (layer), dan tiap frame GPU cuma menggeser lapisan itu.
Biaya Layout ikut naik dengan jumlah elemen. Di Lab hitungannya 0,008 ms per elemen: halaman 600 elemen, left cuma 9,8 ms dan masih lolos. Halaman 3.000 elemen, left 29,0 ms. Biaya transform tetap 1,0 ms berapa pun elemennya.
Satu keuntungan lagi: animasi CSS untuk transform dan opacity bisa dijalankan compositor, di luar main thread. Jadi animasinya tetap mulus walau main thread sedang sibuk menjalankan JavaScript.
Kode: kotak geser yang mulus
<style>
.box {
width: 100px;
height: 100px;
background: #2563eb;
will-change: transform;
animation: geser 2s ease-in-out infinite alternate;
}
@keyframes geser {
from {
transform: translateX(0);
}
to {
transform: translateX(200px);
}
}
</style>
<div class="box"></div>
@keyframes gesermendefinisikan langkah gerakannya: dari (from) ke (to). Namanya bebas, asal sama dengan yang dipanggil dianimation.transform: translateX(200px)menggeser kotak 200 px ke kanan secara visual. Tempat aslinya di layout gak berubah.animation: geser 2s ease-in-out infinite alternatemenjalankannya: nama dulu, lalu durasi 2 detik, percepatanease-in-out, diulang terus (infinite), bolak-balik (alternate).will-change: transformmemberi tahu browser dari awal bahwatransformelemen ini akan berubah. Browser menyiapkan layer-nya sebelum animasi mulai, jadi frame pertama gak tersendat.
Versi yang patah-patah cuma beda di isi @keyframes:
.box {
position: relative;
animation: geser-buruk 2s ease-in-out infinite alternate;
}
@keyframes geser-buruk {
from { left: 0; }
to { left: 200px; } /* Layout dan Paint di tiap frame */
}
Aturan praktis
| Mau bikin | Pakai | Hindari |
|---|---|---|
| Geser posisi | transform: translate() |
left, top, margin |
| Perbesar atau perkecil | transform: scale() |
width, height |
| Putar | transform: rotate() |
|
| Muncul atau hilang pelan-pelan | opacity |
visibility, display |
will-change: secukupnya
Tiap layer makan memori GPU, kira-kira lebar × tinggi × 4 byte (dikali lagi di layar HP yang kerapatan pikselnya tinggi). Kotak 100 × 100 px cuma 40 KB. Tapi 200 kartu 400 × 300 px yang semuanya diberi will-change butuh sekitar 96 MB. HP murah bisa kehabisan memori, dan halamannya malah jadi lambat.
- Pasang
will-changehanya di elemen yang memang akan dianimasikan. - Untuk animasi yang jarang, pasang lewat class sesaat sebelum animasi dan copot sesudahnya.
- Jangan pernah
* { will-change: transform; }.
⚠️ Jebakan yang sering ketemu
- Animasi pakai
left,top,width, atauheight. Tiap frame menjalankan Layout dan Paint. Geser dengantranslateX()di dalam@keyframes. - Nama animasi ditaruh di belakang.
animation: 2s gesertetap valid CSS, tapi latihan ini meminta nama di depan:animation: geser 2s .... Dananimation: noneberarti gak ada animasi. will-changedi semua elemen. Layer berlebihan menghabiskan memori GPU.- Membaca ukuran setelah mengubah style, berulang-ulang. Misalnya mengubah
style.left, lalu langsung membacaoffsetWidth, di dalam loop. Browser terpaksa menjalankan Layout di tiap putaran. Ini disebut layout thrashing: kumpulkan semua pembacaan dulu, baru semua perubahan.
TL;DR: Animasi mulus butuh tiap frame selesai dalam 16,7 ms (60 fps). left, top, width, dan height menjalankan Layout dan Paint di tiap frame: di halaman 1.500 elemen, left makan 17,0 ms dan jatuh ke 30 fps. transform dan opacity cukup Composite: 1,0 ms, berapa pun elemennya. Tulis gerakannya di @keyframes dengan transform: translateX(), jalankan dengan animation: geser 2s ... (nama di depan), dan beri will-change: transform hanya di elemen yang memang dianimasikan.