Canvas Animation
Animasi di canvas bekerja dengan prinsip yang sama seperti film — menampilkan serangkaian gambar (frame) dengan sangat cepat sehingga terlihat bergerak. Targetnya adalah 60 frame per detik (60 FPS).
requestAnimationFrame — Jantung Animasi
Jangan gunakan setInterval untuk animasi canvas! Gunakan requestAnimationFrame:
function animate() {
// 1. Hapus canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2. Update posisi/state
// ...
// 3. Gambar ulang
// ...
// 4. Minta frame berikutnya
requestAnimationFrame(animate);
}
// Mulai animasi
requestAnimationFrame(animate);
Mengapa requestAnimationFrame?
- Otomatis sinkron dengan refresh rate monitor (biasanya 60 FPS)
- Otomatis pause saat tab tidak aktif (hemat CPU/baterai)
- Lebih smooth dari
setInterval
Frame-Based vs Time-Based (penting, baca dulu!)
Sebelum bikin animasi, pahami dua gaya update posisi:
- Frame-based —
x += 3setiap frame. Simpel, tapi kecepatan tergantung FPS. Di HP 120Hz jadi 2x lebih cepat dari monitor 60Hz. Pakai untuk prototype / efek kecil. - Time-based —
x += speed * deltaTime(speed dalam piksel per detik, deltaTime dalam detik). Konsisten di semua device. Pakai untuk game / animasi production.
requestAnimationFrame mengirim parameter timestamp (ms sejak page load) — pakai itu untuk hitung deltaTime:
let lastTime = 0;
function animate(timestamp) {
const deltaTime = (timestamp - lastTime) / 1000; // detik
lastTime = timestamp;
x += speed * deltaTime; // gerak speed piksel/detik, terlepas dari FPS
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Contoh-contoh di bawah pakai frame-based untuk simplicity, tapi untuk particle system yang banyak objek kamu harus pindah ke time-based supaya tidak lag di device lemah.
Bola Memantul (Bouncing Ball)
Contoh klasik animasi canvas — bola yang memantul di dinding:
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
let x = 100, y = 100; // posisi bola
let dx = 3, dy = 2; // kecepatan (piksel per frame)
const radius = 20;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Gambar bola
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = "#3498db";
ctx.fill();
// Update posisi
x += dx;
y += dy;
// Pantulkan di dinding
if (x + radius > canvas.width || x - radius < 0) {
dx = -dx; // balik arah horizontal
}
if (y + radius > canvas.height || y - radius < 0) {
dy = -dy; // balik arah vertikal
}
requestAnimationFrame(animate);
}
animate();
Logika pantulan sederhana: ketika bola menyentuh tepi canvas, kita balik arah kecepatannya (kali -1).
Particle System Sederhana
Particle system adalah kumpulan banyak objek kecil dengan perilaku acak:
const particles = [];
// Buat 100 partikel
for (let i = 0; i < 100; i++) {
particles.push({
x: canvas.width / 2,
y: canvas.height / 2,
vx: (Math.random() - 0.5) * 4, // kecepatan acak
vy: (Math.random() - 0.5) * 4,
radius: Math.random() * 3 + 1,
color: `hsl(${Math.random() * 360}, 70%, 60%)`,
life: 1.0 // opacity, akan berkurang
});
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(p => {
// Update posisi
p.x += p.vx;
p.y += p.vy;
p.life -= 0.005;
// Gambar partikel
if (p.life > 0) {
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.globalAlpha = p.life;
ctx.fill();
}
});
ctx.globalAlpha = 1;
requestAnimationFrame(animate);
}
animate();
Tips Performa
- Selalu gunakan
clearRectuntuk menghapus — jangan buat canvas baru - Untuk background statis, gunakan 2 canvas bertumpuk (satu untuk background, satu untuk animasi)
- Kurangi operasi
beginPath/fill/stroke— batch gambar serupa - Gunakan
ctx.save()danctx.restore()untuk menyimpan/mengembalikan state context