requestAnimationFrame — Web Animation

requestAnimationFrame requestAnimationFrame (disingkat rAF) adalah API browser untuk menjalankan animasi JavaScript yang sinkron dengan refresh rate layar (bias

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:

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.

Yang akan kamu pelajari