"use client"; import { useEffect, useRef } from "react"; interface Particle { x: number; y: number; vx: number; vy: number; size: number; opacity: number; } export function ParticleBg() { const canvasRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; let animationId: number; const particles: Particle[] = []; const particleCount = 60; const resize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }; const createParticle = (): Particle => ({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, vx: (Math.random() - 0.5) * 0.3, vy: (Math.random() - 0.5) * 0.3, size: Math.random() * 1.5 + 0.5, opacity: Math.random() * 0.3 + 0.1, }); const init = () => { resize(); particles.length = 0; for (let i = 0; i < particleCount; i++) { particles.push(createParticle()); } }; const animate = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); for (const p of particles) { p.x += p.vx; p.y += p.vy; if (p.x < 0 || p.x > canvas.width) p.vx *= -1; if (p.y < 0 || p.y > canvas.height) p.vy *= -1; ctx.beginPath(); ctx.arc(p.x, p.y, p.size, 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 < 150) { ctx.beginPath(); ctx.moveTo(particles[i].x, particles[i].y); ctx.lineTo(particles[j].x, particles[j].y); ctx.strokeStyle = `rgba(0, 240, 255, ${0.05 * (1 - dist / 150)})`; ctx.lineWidth = 0.5; ctx.stroke(); } } } animationId = requestAnimationFrame(animate); }; init(); animate(); window.addEventListener("resize", resize); return () => { cancelAnimationFrame(animationId); window.removeEventListener("resize", resize); }; }, []); return ( ); }