import { useEffect, useRef } from "react"; import "./Lightbar.css"; class Particle { x = 0; y = 0; radius = 0; direction = 0; speed = 0; lifetime = 0; ran = 0; constructor(canvas: HTMLCanvasElement) { this.reset(canvas); this.initialize(canvas); } reset(canvas: HTMLCanvasElement) { this.x = Math.round((Math.random() * canvas.width) / 2 + canvas.width / 4); this.y = Math.random() * 100 + 5; this.radius = 1 + Math.floor(Math.random() * 0.5); this.direction = -((Math.random() * Math.PI) / 2) + Math.PI / 4; this.speed = 0.02 + Math.random() * 0.08; const second = 60; this.lifetime = second * 3 + Math.random() * (second * 30); this.ran = 0; } initialize(canvas: HTMLCanvasElement) { this.ran = Math.random() * this.lifetime; const baseSpeed = this.speed; this.speed = Math.random() * this.lifetime * baseSpeed; this.update(canvas); this.speed = baseSpeed; } update(canvas: HTMLCanvasElement) { this.ran += 1; const addX = this.speed * Math.sin(this.direction); const addY = this.speed * Math.cos(this.direction); this.x += addX; this.y += addY; if (this.ran > this.lifetime) { this.reset(canvas); } } render(canvas: HTMLCanvasElement) { const ctx = canvas.getContext("2d"); if (!ctx) return; ctx.save(); ctx.beginPath(); const x = this.ran / this.lifetime; const o = (x - x * x) * 4; ctx.globalAlpha = Math.max(0, o * 0.8); ctx.ellipse( this.x, this.y, this.radius, this.radius * 1.5, this.direction, 0, Math.PI * 2 ); ctx.fillStyle = "white"; ctx.fill(); ctx.restore(); } } function ParticlesCanvas() { const canvasRef = useRef(null); useEffect(() => { if (!canvasRef.current) return; const canvas = canvasRef.current; const particles: Particle[] = []; canvas.width = canvas.scrollWidth; canvas.height = canvas.scrollHeight; for (let i = 0; i < 20; i += 1) { const particle = new Particle(canvas); particles.push(particle); } let shouldTick = true; let handle: ReturnType | null = null; function particlesLoop() { const ctx = canvas.getContext("2d"); if (!ctx) return; if (shouldTick) { for (const particle of particles) { particle.update(canvas); } shouldTick = false; } canvas.width = canvas.scrollWidth; canvas.height = canvas.scrollHeight; for (const particle of particles) { particle.render(canvas); } handle = requestAnimationFrame(particlesLoop); } const interval = setInterval(() => { shouldTick = true; }, 1e3 / 120); // tick 120 times a sec particlesLoop(); return () => { if (handle) cancelAnimationFrame(handle); clearInterval(interval); }; }, []); return ; } export function Lightbar(props: { className?: string }) { return (
); }