Animation Performance & Best Practices
Animasi yang lambat atau jank lebih buruk daripada tidak ada animasi sama sekali. Memahami bagaimana browser merender animasi akan membantumu membuat animasi yang smooth di semua device.
Rendering Pipeline Browser
Setiap frame yang browser render melewati pipeline:
JavaScript → Style → Layout → Paint → Composite
- JavaScript — kalkulasi dan perubahan DOM
- Style — hitung CSS yang berlaku
- Layout — hitung posisi dan ukuran setiap elemen
- Paint — gambar piksel (warna, shadow, text)
- Composite — gabungkan layer dan tampilkan ke layar
Repaint vs Reflow
Reflow (layout) terjadi saat kamu mengubah properti yang mempengaruhi posisi atau ukuran elemen:
/* TRIGGER REFLOW — mahal! */
width, height, margin, padding,
top, left, right, bottom,
font-size, display, position, float
Repaint terjadi saat kamu mengubah properti visual tanpa mengubah layout:
/* TRIGGER REPAINT — cukup mahal */
background-color, color, visibility,
border-color, box-shadow, outline
Compositor-only Properties
Properti yang hanya membutuhkan compositing — paling murah dan paling smooth:
/* COMPOSITOR ONLY — sangat murah! */
transform /* translate, rotate, scale */
opacity
/* Selalu prioritaskan dua properti ini untuk animasi */
/* BURUK — trigger reflow setiap frame */
.animate-bad {
transition: left 0.3s, top 0.3s, width 0.3s;
}
.animate-bad:hover {
left: 100px;
top: 50px;
width: 200px;
}
/* BAIK — hanya compositor, no reflow */
.animate-good {
transition: transform 0.3s;
}
.animate-good:hover {
transform: translate(100px, 50px) scale(1.2);
}
will-change — Gunakan dengan Hati-hati
/* BENAR — set sebelum animasi, hapus setelahnya */
.card:hover {
will-change: transform;
}
/* SALAH — jangan set pada semua elemen */
* { will-change: transform, opacity; }
/* SALAH — terlalu banyak properti */
.element {
will-change: transform, opacity, top, left, width, height;
}
Setiap elemen dengan will-change mendapat layer terpisah di GPU. Terlalu banyak layer = boros memori dan justru bisa memperlambat.
prefers-reduced-motion
Wajib hormati preferensi user yang sensitif terhadap gerakan:
/* Matikan animasi untuk user yang memilih reduced motion */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
// Cek di JavaScript
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches;
if (prefersReducedMotion) {
// Skip animasi atau gunakan alternatif yang lebih subtle
gsap.globalTimeline.timeScale(100); // instant
}
Performance Debugging Tools
Chrome DevTools Performance Tab
- Buka DevTools → Performance tab
- Klik record, lakukan interaksi, stop record
- Lihat frame rate (harus konsisten 60fps)
- Cari long tasks (blok merah) yang menyebabkan jank
DevTools Rendering Tab
- Buka DevTools → tekan Ctrl+Shift+P → ketik "rendering"
- Aktifkan Paint flashing — area yang di-repaint akan flash hijau
- Aktifkan Layout shift regions — area yang reflow akan flash biru
- Aktifkan Layer borders — lihat compositor layers
Best Practices Checklist
Duration & Timing
- UI transitions: 150-300ms
- Entry animations: 300-500ms
- Exit animations: 150-250ms (lebih cepat dari entry)
- Complex sequences: max 800ms total
What to Animate
- DO: transform, opacity
- AVOID: width, height, top, left, margin, padding
- NEVER: animasi yang mengubah layout pada banyak elemen sekaligus
Accessibility
- Selalu sediakan
prefers-reduced-motionfallback - Jangan gunakan animasi yang bisa memicu seizure (flashing >3x/detik)
- Pastikan animasi tidak menghalangi interaksi user
Mobile Considerations
- Mobile device punya GPU lebih lemah — kurangi kompleksitas
- Hindari box-shadow animation di mobile
- Test di device low-end, bukan hanya simulator
Contoh: Optimized Fade-in on Scroll
// Optimized IntersectionObserver + CSS transition
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease-out, transform 0.5s ease-out;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.fade-in {
opacity: 1;
transform: none;
transition: none;
}
}
Animasi yang baik adalah animasi yang user rasakan tapi tidak sadari. Jika user memperhatikan animasinya, mungkin animasinya terlalu berlebihan atau terlalu lambat.