requestAnimationFrame
requestAnimationFrame (disingkat rAF) adalah API browser untuk menjalankan animasi JavaScript yang sinkron dengan refresh rate layar (biasanya 60fps). Ini adalah cara paling efisien untuk membuat animasi berbasis JavaScript.
Cara Kerja rAF
function animate() {
// Update posisi / state
// ...
// Minta browser menjalankan frame berikutnya
requestAnimationFrame(animate);
}
// Mulai animasi
requestAnimationFrame(animate);
Browser akan memanggil callback kamu tepat sebelum repaint berikutnya, biasanya 60 kali per detik (60fps). Ini berbeda dengan setInterval yang berjalan di waktu tetap tanpa peduli apakah browser siap merender.
Perbandingan dengan setInterval
// BURUK — setInterval tidak sinkron dengan render cycle
setInterval(() => {
element.style.left = position + 'px';
position += 1;
}, 16); // ~60fps tapi tidak akurat
// BAIK — rAF sinkron dengan render
function animate() {
element.style.transform = \`translateX(${position}px)\`;
position += 2;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Keuntungan rAF:
- Sinkron dengan refresh rate — tidak ada frame yang terbuang
- Otomatis pause saat tab tidak aktif — hemat baterai
- Batch updates — browser mengoptimasi DOM changes
- Smoother — tidak ada jank karena timing mismatch
Time-based Animation
Jangan pakai kecepatan konstan per frame — gunakan waktu agar animasi konsisten di semua device:
let startTime = null;
function animate(currentTime) {
if (!startTime) startTime = currentTime;
// Hitung progress (0 sampai 1)
const elapsed = currentTime - startTime;
const duration = 2000; // 2 detik
const progress = Math.min(elapsed / duration, 1);
// Terapkan animasi berdasarkan progress
const distance = 300; // pixel
element.style.transform = \`translateX(${progress * distance}px)\`;
// Lanjut sampai selesai
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Membatalkan Animasi
let animationId;
function animate() {
// ... update ...
animationId = requestAnimationFrame(animate);
}
// Mulai
animationId = requestAnimationFrame(animate);
// Berhenti
cancelAnimationFrame(animationId);
Game Loop Pattern
Pattern klasik untuk game atau animasi interaktif:
let lastTime = 0;
function gameLoop(currentTime) {
// Delta time dalam detik
const deltaTime = (currentTime - lastTime) / 1000;
lastTime = currentTime;
// Update logic (physics, AI, etc)
update(deltaTime);
// Render visual
render();
// Next frame
requestAnimationFrame(gameLoop);
}
function update(dt) {
// Gerakkan objek berdasarkan waktu, bukan frame
player.x += player.speed * dt;
player.y += player.speed * dt;
}
function render() {
// Gambar ke canvas atau update DOM
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(player.x, player.y, 50, 50);
}
// Mulai game loop
requestAnimationFrame(gameLoop);
Contoh: Smooth Counter
function animateCounter(element, target, duration) {
let startTime = null;
const startValue = 0;
function step(currentTime) {
if (!startTime) startTime = currentTime;
const progress = Math.min(
(currentTime - startTime) / duration, 1
);
// Easing: ease-out quad
const eased = 1 - (1 - progress) * (1 - progress);
const current = Math.round(
startValue + (target - startValue) * eased
);
element.textContent = current.toLocaleString();
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
// Animasi angka dari 0 ke 10000 dalam 2 detik
animateCounter(document.getElementById('counter'), 10000, 2000);
Easing dengan rAF
// Kumpulan easing functions
const easings = {
linear: t => t,
easeInQuad: t => t * t,
easeOutQuad: t => t * (2 - t),
easeInOutQuad: t => t < 0.5
? 2 * t * t
: -1 + (4 - 2 * t) * t,
easeOutCubic: t => (--t) * t * t + 1,
easeOutElastic: t => {
const p = 0.3;
return Math.pow(2, -10 * t) *
Math.sin((t - p / 4) * (2 * Math.PI) / p) + 1;
},
};
function animateWithEasing(element, from, to, duration, easing) {
let start = null;
function step(time) {
if (!start) start = time;
const progress = Math.min((time - start) / duration, 1);
const easedProgress = easing(progress);
const value = from + (to - from) * easedProgress;
element.style.transform = \`translateX(${value}px)\`;
if (progress < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
requestAnimationFrame adalah fondasi semua animation library JavaScript. Memahaminya akan membantu kamu debug dan mengoptimasi animasi apapun.