// Ember particle canvas — slow-rising sparks function EmberCanvas({ intensity = 1 }) { const ref = React.useRef(null); const rafRef = React.useRef(0); React.useEffect(() => { if (intensity === 0) return; const canvas = ref.current; if (!canvas) return; const ctx = canvas.getContext("2d"); let w = 0, h = 0, dpr = window.devicePixelRatio || 1; const resize = () => { const rect = canvas.parentElement.getBoundingClientRect(); w = rect.width; h = rect.height; 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); }; resize(); window.addEventListener("resize", resize); const count = Math.floor(60 * intensity); const particles = Array.from({ length: count }, () => spawn(w, h, true)); function spawn(W, H, atRandom) { return { x: Math.random() * W, y: atRandom ? Math.random() * H : H + 20, vx: (Math.random() - 0.5) * 0.25, vy: -(0.25 + Math.random() * 0.7), r: 0.5 + Math.random() * 1.6, life: 0, maxLife: 200 + Math.random() * 400, flicker: Math.random() * Math.PI * 2, }; } const draw = () => { ctx.clearRect(0, 0, w, h); for (const p of particles) { p.x += p.vx + Math.sin((p.life + p.flicker) * 0.03) * 0.3; p.y += p.vy; p.life += 1; if (p.y < -10 || p.life > p.maxLife) Object.assign(p, spawn(w, h, false)); const lifeT = p.life / p.maxLife; const alpha = (1 - lifeT) * (0.4 + Math.sin(p.flicker + p.life * 0.1) * 0.3); const radius = p.r * (1 - lifeT * 0.4); const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, radius * 6); grad.addColorStop(0, `rgba(255, 170, 80, ${alpha})`); grad.addColorStop(0.4, `rgba(255, 100, 40, ${alpha * 0.4})`); grad.addColorStop(1, "rgba(255, 60, 20, 0)"); ctx.fillStyle = grad; ctx.beginPath(); ctx.arc(p.x, p.y, radius * 6, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = `rgba(255, 220, 160, ${alpha})`; ctx.beginPath(); ctx.arc(p.x, p.y, radius, 0, Math.PI * 2); ctx.fill(); } rafRef.current = requestAnimationFrame(draw); }; draw(); return () => { cancelAnimationFrame(rafRef.current); window.removeEventListener("resize", resize); }; }, [intensity]); if (intensity === 0) return null; return ; } window.EmberCanvas = EmberCanvas;