"use client"; import { useEffect, useRef } from "react"; const MAX_PARTICLES = 40; type Particle = { x: number; y: number; vy: number; vx: number; size: number; alpha: number; glow: number; }; type Props = { active: boolean; className?: string; }; export function HeroRiftParticles({ active, className }: Props) { const canvasRef = useRef(null); useEffect(() => { if (!active) return; const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; let raf = 0; let particles: Particle[] = []; const resize = () => { const parent = canvas.parentElement; if (!parent) return; const dpr = Math.min(window.devicePixelRatio ?? 1, 2); const w = parent.clientWidth; const h = parent.clientHeight; canvas.width = w * dpr; canvas.height = h * dpr; canvas.style.width = `${w}px`; canvas.style.height = `${h}px`; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); const count = Math.min(MAX_PARTICLES, Math.floor((w * h) / 8500)); particles = Array.from({ length: count }, () => { const large = Math.random() < 0.18; return { x: w * (0.28 + Math.random() * 0.44), y: h * (0.38 + Math.random() * 0.42), vy: -0.12 - Math.random() * (large ? 0.22 : 0.14), vx: (Math.random() - 0.5) * 0.04, size: large ? 2.2 + Math.random() * 3.2 : 1.2 + Math.random() * 2.4, alpha: 0.35 + Math.random() * 0.45, glow: large ? 14 + Math.random() * 10 : 8 + Math.random() * 6, }; }); }; const tick = () => { const w = canvas.clientWidth; const h = canvas.clientHeight; ctx.clearRect(0, 0, w, h); for (const p of particles) { p.x += p.vx; p.y += p.vy; if (p.y < h * 0.18) { p.y = h * (0.52 + Math.random() * 0.35); p.x = w * (0.28 + Math.random() * 0.44); } if (p.x < w * 0.2) p.vx += 0.008; if (p.x > w * 0.8) p.vx -= 0.008; ctx.save(); ctx.shadowBlur = p.glow; ctx.shadowColor = "rgba(255, 200, 90, 0.85)"; ctx.beginPath(); const g = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 1.8); g.addColorStop(0, `rgba(255, 220, 140, ${p.alpha})`); g.addColorStop(0.45, `rgba(255, 191, 80, ${p.alpha * 0.75})`); g.addColorStop(1, `rgba(45, 122, 82, 0)`); ctx.fillStyle = g; ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; ctx.strokeStyle = `rgba(255, 235, 180, ${p.alpha * 0.55})`; ctx.lineWidth = 0.6; ctx.stroke(); ctx.restore(); } raf = requestAnimationFrame(tick); }; resize(); window.addEventListener("resize", resize); raf = requestAnimationFrame(tick); return () => { cancelAnimationFrame(raf); window.removeEventListener("resize", resize); }; }, [active]); if (!active) return null; return ; }