/* ═══════════════════════════════════════════════════════════════ ParticleBackground — Animated cyberpunk network visualization Canvas-based particle system with connecting lines ═══════════════════════════════════════════════════════════════ */ import { useEffect, useRef } from 'react'; const PARTICLE_COUNT = 50; const CONNECTION_DISTANCE = 140; const PARTICLE_SPEED = 0.25; export default function ParticleBackground() { const canvasRef = useRef(null); const animRef = useRef(null); const particlesRef = useRef([]); useEffect(() => { const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); let width = window.innerWidth; let height = window.innerHeight; canvas.width = width; canvas.height = height; // Initialize particles particlesRef.current = Array.from({ length: PARTICLE_COUNT }, () => ({ x: Math.random() * width, y: Math.random() * height, vx: (Math.random() - 0.5) * PARTICLE_SPEED, vy: (Math.random() - 0.5) * PARTICLE_SPEED, radius: Math.random() * 1.5 + 0.5, opacity: Math.random() * 0.3 + 0.1, })); function animate() { ctx.clearRect(0, 0, width, height); const particles = particlesRef.current; // Update & draw particles particles.forEach((p) => { p.x += p.vx; p.y += p.vy; // Wrap around edges if (p.x < 0) p.x = width; if (p.x > width) p.x = 0; if (p.y < 0) p.y = height; if (p.y > height) p.y = 0; // Draw particle ctx.beginPath(); ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); ctx.fillStyle = `rgba(0, 240, 255, ${p.opacity})`; ctx.fill(); }); // Draw connections for (let i = 0; i < particles.length; i++) { for (let j = i + 1; j < particles.length; j++) { const dx = particles[i].x - particles[j].x; const dy = particles[i].y - particles[j].y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < CONNECTION_DISTANCE) { const opacity = (1 - dist / CONNECTION_DISTANCE) * 0.15; ctx.beginPath(); ctx.moveTo(particles[i].x, particles[i].y); ctx.lineTo(particles[j].x, particles[j].y); ctx.strokeStyle = `rgba(0, 240, 255, ${opacity})`; ctx.lineWidth = 0.5; ctx.stroke(); } } } animRef.current = requestAnimationFrame(animate); } animate(); const handleResize = () => { width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; }; window.addEventListener('resize', handleResize); return () => { cancelAnimationFrame(animRef.current); window.removeEventListener('resize', handleResize); }; }, []); return ( ); }