Apa itu Web Animation? — Web Animation

Pernah pake aplikasi yang transisi-nya patah-patah, klik tombol langsung keluar tanpa feedback, atau modal muncul tiba-tiba bikin kaget? Vs aplikasi seperti…

Animasi web adalah nilai CSS yang berubah sedikit demi sedikit, lalu digambar ulang berkali-kali dalam satu detik. Bayangin flipbook: tiap halamannya gambar diam, dan waktu dibalik cepat, gambarnya terlihat bergerak. Di browser, satu halaman itu namanya frame. Animasi terasa mulus kalau browser selalu sempat menyelesaikan frame berikutnya sebelum waktunya ditampilkan.

🧪 Tebakan cepat: Kartu Kamar Teras muncul dengan animasi 300 ms. Pak Hadi membukanya di HP 60 Hz, Bu Sri di HP 120 Hz. Di HP Bu Sri animasinya selesai lebih cepat? Dan berapa lama browser boleh menggambar satu frame di tiap HP? Jawabannya ada di bagian "Frame budget: 16,7 ms atau 8,3 ms".

Halaman yang akan kita gerakkan

Sepanjang jalur ini kita memakai halaman pesan Homestay Kabut, homestay kecil milik Bu Sri di Dieng, yang sudah kamu kenal dari jalur UI/UX. Isinya: kartu Kamar Teras (Rp 350.000 per malam), Kamar Loteng (Rp 275.000), dan Kamar Kebun (Rp 425.000); tombol 1, 2, 3 malam; total harga ("Total 2 malam: Rp 700.000"); tombol hijau "Pesan kamar, bayar di tempat"; dialog konfirmasi <dialog>; dan toast "Pesanan terkirim". Warnanya: hijau #15803d (hover #166534), teks #1f2937, teks kedua #475569, garis #e2e8f0.

Kapan dan kenapa sebuah momen perlu animasi sudah dibahas di pelajaran Microinteractions & Animation di jalur UI/UX. Jalur ini membahas caranya, sampai ke angka. Ini semua animasi di halaman itu:

Momen Yang bergerak Durasi Easing
Tombol malam ditekan transform: scale(0.96) 100 ms ease-out
Tombol malam terpilih background-color jadi #15803d 150 ms ease-out
Total harga berganti angka baru naik 4 px sambil muncul 200 ms ease-out
Kartu kamar masuk opacity 0 ke 1, naik 16 px 300 ms ease-out
Dialog konfirmasi buka: opacity 0 ke 1, scale(0.96) ke 1 buka 200 ms, tutup 150 ms buka ease-out, tutup ease-in
Toast "Pesanan terkirim" naik dari bawah, tampil 5 detik masuk 200 ms, keluar 150 ms masuk ease-out, keluar ease-in
Galeri foto kamar geser satu foto 300 ms ease-in-out

Lima keluarga animasi

Animasi yang sama, kartu Kamar Teras masuk (300 ms, ease-out, opacity 0 ke 1 sambil naik 16 px), bisa ditulis dengan lima cara:

/* 1. CSS transition: bergerak di antara dua state saat nilainya berubah */
.card { opacity: 0; transform: translateY(16px);
        transition: opacity 300ms ease-out, transform 300ms ease-out; }
.card.in { opacity: 1; transform: none; }

/* 2. CSS animation: naskah @keyframes yang jalan sendiri begitu aturannya berlaku */
@keyframes card-in { from { opacity: 0; transform: translateY(16px); } }
.card { animation: card-in 300ms ease-out; }
// 3. Web Animations API: keyframes yang sama dari JavaScript, bisa di-pause dan di-reverse
const anim = card.animate(
  [{ opacity: 0, transform: 'translateY(16px)' }, { opacity: 1, transform: 'none' }],
  { duration: 300, easing: 'ease-out' }
);

// 4. requestAnimationFrame: kamu sendiri yang menghitung nilai di tiap frame
const start = performance.now();
function frame(now) {
  const t = Math.min((now - start) / 300, 1); // waktu, 0 sampai 1
  const p = 1 - (1 - t) ** 2;                 // melambat di akhir, mirip ease-out
  card.style.opacity = p;
  card.style.transform = `translateY(${16 * (1 - p)}px)`;
  if (t < 1) requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

// 5. Library (misalnya Motion): satu panggilan, di baliknya Web Animations API atau rAF
animate(card, { opacity: [0, 1], transform: ['translateY(16px)', 'none'] }, { duration: 0.3, ease: 'easeOut' });
Keluarga Cocok untuk Di halaman Bu Sri
CSS transition dua state yang dipicu perubahan: hover, focus, kelas, atribut tombol malam, warna tombol Pesan
CSS animation (@keyframes) lebih dari dua langkah, berulang, atau jalan sendiri saat muncul toast, titik "Memesan...", kartu masuk
Web Animations API butuh kontrol: pause, reverse, tahu kapan selesai, nilai dari data dialog yang dibatalkan di tengah jalan
requestAnimationFrame / canvas nilai dihitung sendiri tiap frame: fisika, angka berjalan, canvas total harga yang menghitung naik
Library (Motion, GSAP, Lottie) urutan panjang, gesture, animasi komponen React, file After Effects galeri foto dengan geser jari

Para pengajar sering meringkasnya jadi tiga pilar: CSS transition, CSS animation (@keyframes), dan JavaScript. Pilar ketiga berisi keluarga 3 sampai 5: Web Animations API sebagai API resmi browser, requestAnimationFrame, dan library yang dibangun di atas keduanya.

Frame budget: 16,7 ms atau 8,3 ms

Layar menampilkan gambar baru sejumlah kali per detik. Angka itu disebut refresh rate, satuannya Hz. Satu detik = 1.000 ms, jadi waktu untuk satu frame adalah 1.000 dibagi refresh rate:

Layar Waktu per frame Kartu 300 ms jadi
60 Hz 1.000 / 60 = 16,7 ms 18 frame
90 Hz 1.000 / 90 = 11,1 ms 27 frame
120 Hz 1.000 / 120 = 8,3 ms 36 frame

Itu jawaban tebakannya: animasinya tetap 300 ms di kedua HP, karena durasi diukur dengan waktu, bukan dengan jumlah frame. HP Bu Sri menggambar 36 frame, jadi lebih halus, tapi tiap frame cuma punya 8,3 ms. Itu frame budget. Kalau kerja browser untuk satu frame lewat dari budget, frame itu terlewat (dropped frame) dan geraknya tersendat. Gerak yang tersendat ini disebut jank. Artikel "Rendering performance" di web.dev menghitung budget 60 Hz sebagai 16,66 ms, lalu menyarankan kerjamu selesai dalam sekitar 10 ms, karena browser juga butuh waktu untuk dirinya sendiri.

Lima langkah menggambar satu frame

Untuk tiap frame, browser bisa melewati lima langkah (web.dev menyebutnya pixel pipeline):

  1. JavaScript: kode yang mengubah sesuatu, misalnya menambah kelas in ke kartu.
  2. Style: menghitung aturan CSS mana yang berlaku ke elemen mana.
  3. Layout: menghitung posisi dan ukuran tiap kotak. Kalau kartu bertambah tinggi, semua yang di bawahnya ikut dihitung ulang.
  4. Paint: mewarnai piksel: teks, warna latar, bayangan, ke dalam satu atau beberapa lapisan (layer).
  5. Composite: menyusun layer-layer itu jadi satu gambar di layar.

Properti yang kamu animasikan menentukan langkah mana yang diulang di setiap frame:

Properti Diulang tiap frame
top, left, width, height, margin Style, Layout, Paint, Composite
box-shadow, color, border-color Style, Paint, Composite
transform, opacity Composite saja

Kami mengukurnya dengan Performance trace di Chromium 147: kartu naik 16 px selama 300 ms. Lewat top, layout dihitung 35 sampai 37 kali dan paint 70 sampai 72 kali. Lewat transform, layout 1 kali dan paint 2 kali, keduanya di awal saja. Detailnya ada di pelajaran Transform & Composite.

Langkah composite dikerjakan di thread terpisah, compositor thread. Animasi transform dan opacity dari CSS atau Web Animations API bisa jalan sepenuhnya di sana, tanpa menunggu main thread (thread tempat JavaScript dan layout jalan). Kami membuat main thread sibuk selama 500 ms di tengah animasi di Chromium 147: CSS transition tetap menggambar 61 frame, Web Animations API 60 frame, sedangkan versi requestAnimationFrame cuma 1 frame. Geraknya membeku, lalu melompat.

CSS atau JavaScript?

Mulai dari CSS. Pindah ke JavaScript kalau animasinya butuh salah satu dari ini:

Alasannya bukan soal thread. Justru requestAnimationFrame selalu jalan di main thread, jadi ikut tersendat kalau main thread sibuk, seperti hasil ukur di atas.

prefers-reduced-motion sejak hari pertama

Ada orang yang pusing atau mual melihat gerakan di layar. Mereka menyalakan "Kurangi gerakan" di pengaturan HP atau komputernya, dan browser meneruskannya sebagai media query prefers-reduced-motion: reduce (didukung sejak Chrome 74, Firefox 63, Safari 10.1, menurut MDN). Hormati sejak animasi pertama: buang gerakannya, sisakan fade singkat.

@media (prefers-reduced-motion: reduce) {
  .card { transform: none; transition: opacity 150ms linear; }
}
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
card.animate(
  reduce ? [{ opacity: 0 }, { opacity: 1 }]
         : [{ opacity: 0, transform: 'translateY(16px)' }, { opacity: 1, transform: 'none' }],
  { duration: reduce ? 150 : 300, easing: 'ease-out' }
);

⚠️ Jebakan yang sering ketemu

TL;DR: Animasi web adalah nilai CSS yang berubah sedikit demi sedikit, digambar per frame. Lima keluarga: CSS transition (dua state), CSS animation @keyframes (naskah bertahap), Web Animations API (keyframes dari JS dengan pause dan reverse), requestAnimationFrame (hitung sendiri tiap frame), dan library. Ringkasnya tiga pilar: transition, @keyframes, dan JavaScript. Frame budget = 1.000 ms dibagi refresh rate: 16,7 ms di 60 Hz, 8,3 ms di 120 Hz; animasi 300 ms tetap 300 ms, cuma jumlah frame-nya yang beda (18 atau 36). Tiap frame melewati JavaScript, Style, Layout, Paint, Composite; transform dan opacity cukup sampai composite dan bisa jalan di compositor thread walau main thread sibuk, sedangkan rAF ikut tersendat. Pakai JavaScript kalau butuh logika dinamis, interaksi rumit, atau kontrol playback. Hormati prefers-reduced-motion sejak awal: buang gerakan, sisakan fade.

Yang akan kamu pelajari