/* ========================================== ANIMATION LOGIN - PARTICLE BACKGROUND SYSTEM ========================================== fungsi: - Class Particle: Objek partikel dengan posisi & kecepatan - Even Distribution: Distribusi partikel merata (15x10 grid) - Animate Loop: Pergerakan smooth dengan requestAnimationFrame ========================================== */ /* ========================================== SETUP CONTAINER & CONFIG ========================================== */ const particlesContainer = document.getElementById('particles'); const particleCount = 150; // Total partikel yang dibuat const particles = []; // Array untuk simpan semua partikel /* ========================================== CLASS PARTICLE - Blueprint untuk setiap partikel ========================================== Properties: - x, y: Posisi partikel - vx, vy: Kecepatan horizontal & vertikal (velocity) - size: Ukuran partikel (2-5px) - color: Warna neon random ========================================== */ class Particle { constructor() { // Buat element DOM untuk partikel this.element = document.createElement('div'); this.element.className = 'particle'; this.reset(); // Initialize posisi & properti particlesContainer.appendChild(this.element); } /* ========================================== RESET - Set/reset properti partikel ========================================== */ reset() { // Posisi random di seluruh layar this.x = Math.random() * window.innerWidth; this.y = Math.random() * window.innerHeight; // Kecepatan random (-0.6 sampai +0.6 pixel/frame) this.vx = (Math.random() - 0.5) * 1.2; this.vy = (Math.random() - 0.5) * 1.2; // Ukuran random (2-5px) this.size = Math.random() * 3 + 2; // Array warna neon untuk partikel const colors = [ '#00d9ff', // Cyan '#ff00ff', // Magenta '#00ffff', // Cyan bright '#ff0080', // Pink '#9d00ff', // Purple '#00ff88' // Green ]; // Pilih warna random dari array const color = colors[Math.floor(Math.random() * colors.length)]; this.element.style.background = color; this.element.style.boxShadow = `0 0 15px ${color}`; // Glow effect this.element.style.width = `${this.size}px`; this.element.style.height = `${this.size}px`; this.updatePosition(); // Update posisi di DOM } /* ========================================== UPDATE POSITION - Sync posisi ke DOM ========================================== */ updatePosition() { this.element.style.left = `${this.x}px`; this.element.style.top = `${this.y}px`; } /* ========================================== MOVE - Update posisi berdasarkan velocity ========================================== Fitur: Wrap-around screen (partikel keluar = muncul sisi lain) ========================================== */ move() { // Update posisi berdasarkan kecepatan this.x += this.vx; this.y += this.vy; // ♻️ WRAP-AROUND LOGIC // Kalau keluar dari kiri → muncul dari kanan if (this.x < -10) this.x = window.innerWidth + 10; // Kalau keluar dari kanan → muncul dari kiri if (this.x > window.innerWidth + 10) this.x = -10; // Kalau keluar dari atas → muncul dari bawah if (this.y < -10) this.y = window.innerHeight + 10; // Kalau keluar dari bawah → muncul dari atas if (this.y > window.innerHeight + 10) this.y = -10; this.updatePosition(); // Sync ke DOM } } /* ========================================== EVEN DISTRIBUTION - Distribusi Partikel Merata ========================================== Konsep: Grid 15 kolom x 10 baris (total 150 partikel) Biar nggak menumpuk di satu area ========================================== */ const cols = 15; // Jumlah kolom const rows = 10; // Jumlah baris let particleIndex = 0; // Counter partikel yang sudah dibuat // Loop baris for (let i = 0; i < rows; i++) { // Loop kolom for (let j = 0; j < cols; j++) { // Guard: stop kalau sudah 150 partikel if (particleIndex >= particleCount) break; const particle = new Particle(); // ⚙️ DISTRIBUSI MERATA + RANDOM OFFSET // Formula: (index / total) × lebar/tinggi layar particle.x = (j / cols) * window.innerWidth + (Math.random() - 0.5) * 100; particle.y = (i / rows) * window.innerHeight + (Math.random() - 0.5) * 100; particle.updatePosition(); particles.push(particle); // Simpan ke array particleIndex++; } if (particleIndex >= particleCount) break; } /* ========================================== ANIMATE - Main Animation Loop ========================================== Menggunakan requestAnimationFrame untuk performa optimal (60 FPS, smooth, GPU-accelerated) ========================================== */ function animate() { // Loop semua partikel dan gerakkan particles.forEach(p => p.move()); // Request next frame (recursive call) // Browser otomatis sync dengan refresh rate monitor requestAnimationFrame(animate); } // Start animation loop animate();