Canvas Animation Lanjutan
Canvas 2D API memberikan kontrol piksel penuh untuk animasi — cocok untuk game, data visualisasi, efek partikel, dan animasi yang tidak mungkin dilakukan hanya dengan DOM/CSS.
Konsep Dasar Canvas Loop
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Pastikan resolusi tajam di layar Retina
const dpr = window.devicePixelRatio || 1;
canvas.width = canvas.offsetWidth * dpr;
canvas.height = canvas.offsetHeight * dpr;
ctx.scale(dpr, dpr);
let animationId;
function animate(timestamp) {
// 1. Bersihkan frame sebelumnya
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2. Update state
update(timestamp);
// 3. Gambar semua objek
draw();
// 4. Minta frame berikutnya
animationId = requestAnimationFrame(animate);
}
// Mulai loop
animationId = requestAnimationFrame(animate);
// Bersihkan saat komponen unmount
function stop() {
cancelAnimationFrame(animationId);
}
Sistem Partikel
class Particle {
constructor(x, y) {
this.x = x;
this.y = y;
this.vx = (Math.random() - 0.5) * 4; // kecepatan horizontal
this.vy = (Math.random() - 0.5) * 4; // kecepatan vertikal
this.life = 1; // opacity (1 = penuh)
this.decay = Math.random() * 0.02 + 0.005;
this.radius = Math.random() * 4 + 2;
this.color = \`hsl(${Math.random() * 60 + 20}, 90%, 60%)\`;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.1; // gravitasi
this.life -= this.decay;
}
draw(ctx) {
ctx.save();
ctx.globalAlpha = this.life;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
isAlive() { return this.life > 0; }
}
// Sistem partikel
const particles = [];
function update() {
// Spawn partikel baru
if (Math.random() < 0.3) {
particles.push(new Particle(canvas.width / 2, canvas.height / 2));
}
// Update & hapus partikel yang sudah mati
for (let i = particles.length - 1; i >= 0; i--) {
particles[i].update();
if (!particles[i].isAlive()) particles.splice(i, 1);
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(p => p.draw(ctx));
}
Teknik Optimasi Canvas
// 1. Offscreen canvas — render di background, blit ke layar
const offscreen = document.createElement('canvas');
const offCtx = offscreen.getContext('2d');
// Render heavy graphics ke offscreen
offCtx.drawImage(complexShape, 0, 0);
// Blit ke main canvas (sangat cepat)
ctx.drawImage(offscreen, 0, 0);
// 2. Batching draw calls
// BURUK: ganti style setiap objek
objects.forEach(obj => {
ctx.fillStyle = obj.color; // state change tiap iterasi
ctx.fillRect(obj.x, obj.y, obj.w, obj.h);
});
// BAIK: kelompokkan berdasarkan style
const byColor = groupBy(objects, 'color');
for (const [color, group] of Object.entries(byColor)) {
ctx.fillStyle = color; // state change sekali per warna
ctx.beginPath();
group.forEach(obj => ctx.rect(obj.x, obj.y, obj.w, obj.h));
ctx.fill();
}
// 3. Caching path kompleks
const cachedPath = new Path2D();
cachedPath.arc(0, 0, 50, 0, Math.PI * 2);
// Reuse path di setiap frame
ctx.fill(cachedPath);
Efek Visual Canvas
// Trail effect — jangan clearRect sepenuhnya
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; // semi-transparent overlay
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Glow effect dengan shadow
ctx.shadowColor = '#3b82f6';
ctx.shadowBlur = 20;
ctx.fillStyle = '#60a5fa';
ctx.beginPath();
ctx.arc(x, y, 5, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0; // reset setelahnya
// Gradient animasi
const gradient = ctx.createRadialGradient(x, y, 0, x, y, 100);
gradient.addColorStop(0, 'rgba(59, 130, 246, 0.8)');
gradient.addColorStop(1, 'rgba(59, 130, 246, 0)');
ctx.fillStyle = gradient;
ctx.fillRect(x - 100, y - 100, 200, 200);
Canvas memberikan kebebasan total untuk animasi — dari simulasi fisika hingga efek visual yang tidak mungkin dilakukan dengan DOM. Kuncinya adalah optimasi loop dan batching draw calls.