Partikel Emas Kanvas & Siklus Sinus
Sistem 100 partikel debu emas dan krem yang melayang naik di atas HTML5 Canvas dengan tingkat transparansi mengikuti kurva gelombang sinus.
Membaca sekitar 2 menit
Sistem 100 partikel debu emas dan krem yang melayang naik di atas HTML5 Canvas dengan tingkat transparansi mengikuti kurva gelombang sinus.
Komponen Particles menggambar ulang lingkaran mikro setiap frame dan mendaur ulang objek partikel yang habis masa hidupnya menggunakan Object.assign tanpa membuat array baru.
Penggunaan kurva Math.sin membuat partikel muncul dan memudar secara organik tanpa pernah terlihat berkedip tiba-tiba saat di-reset.
Masalahnya dulu
Meskipun kamera sudah bergerak dinamis, gambar latar di dalam adegan yang sedang diam (seperti saat tamu berhenti membaca kutipan ayat atau menghitung mundur hari) bisa terasa beku. Diperlukan elemen visual hidup yang terus bergerak lembut di latar depan tanpa mengalihkan perhatian dari teks utama.
Menggambar 100 partikel dengan siklus hidup sinus
Komponen Particles merender elemen <canvas> transparan seukuran layar penuh. Di dalamnya terdapat 100 titik partikel campuran warna emas (rgba(201,169,110)) dan krem hangat yang bergerak perlahan ke atas. Alih-alih muncul dan hilang secara mendadak, tingkat opasitas setiap partikel dihitung menggunakan fungsi Math.sin berdasarkan rasio umur partikel (life / max).
Kurva Math.sin(life / max * Math.PI) bernilai 0 di awal, memuncak di angka 1 pada pertengahan umur, dan kembali ke 0 di akhir—formula sempurna untuk efek kunang-kunang yang bernapas alami.
Implementasi di ngodingan ini
Untuk menjaga performa tetap 60fps tanpa memicu Garbage Collector (GC pause), array partikel hanya dibuat satu kali di awal. Saat sebuah partikel keluar dari batas atas layar atau habis masa hidupnya, propertinya langsung ditimpa di tempat menggunakan Object.assign:
const pts = Array.from({ length: 100 }, () => mk(true));
const loop = () => {
ctx.clearRect(0, 0, W, H);
for (const p of pts) {
p.x += p.vx;
p.y += p.vy;
p.life++;
if (p.y < -8 || p.life > p.max) Object.assign(p, mk(false));
const a = Math.sin((p.life / p.max) * Math.PI) * p.a;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = p.gold
? `rgba(201,169,110,${a})`
: `rgba(255,248,238,${a * 0.38})`;
ctx.fill();
}
raf = requestAnimationFrame(loop);
};Jebakan dan detail yang harus dijaga
- Membedakan parameter inisialisasi mk(true) dan daur ulang mk(false): saat pertama kali dimuat, partikel disebar acak di seluruh tinggi layar (Math.random() * H), tetapi saat didaur ulang partikel selalu lahir dari bawah layar (H + 5).
- Memasang pendengar resize pada window untuk memperbarui atribut width dan height kanvas secara dinamis agar bentuk lingkaran partikel tidak gepeng saat orientasi layar ponsel diputar.
- Memberikan pointer-events: none pada elemen canvas agar seratus partikel yang berada di z-index: 10 sama sekali tidak mengganggu interaksi klik maupun sentuhan layar.