import React, { useEffect, useRef, useState, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Play } from 'lucide-react'; export default function StartConfettiButton({ isDraftLike, onStart, className, label = 'Start', }: { isDraftLike: boolean; onStart?: () => void; className?: string; label?: string; }) { const [showConfetti, setShowConfetti] = useState(false); const canvasRef = useRef(null); const rafRef = useRef(null); // ---- Confetti Engine (inline, light) ---- const runConfetti = useCallback(() => { const canvas = canvasRef.current!; const ctx = canvas.getContext('2d')!; const DPR = Math.max(1, Math.min(2, window.devicePixelRatio || 1)); // size canvas const resize = () => { const { innerWidth: w, innerHeight: h } = window; canvas.width = Math.floor(w * DPR); canvas.height = Math.floor(h * DPR); canvas.style.width = `${w}px`; canvas.style.height = `${h}px`; ctx.setTransform(DPR, 0, 0, DPR, 0, 0); }; resize(); const onResize = () => resize(); window.addEventListener('resize', onResize); const colors = [ '#ef4444', // red-500 '#f59e0b', // amber-500 '#10b981', // emerald-500 '#3b82f6', // blue-500 '#8b5cf6', // violet-500 '#ec4899', // pink-500 '#22c55e', // green-500 '#eab308', // yellow-500 ]; type Particle = { x: number; y: number; w: number; h: number; vx: number; vy: number; rot: number; rotSpeed: number; tilt: number; tiltSpeed: number; color: string; alpha: number; decay: number; shape: 'rect' | 'triangle'; }; const rand = (min: number, max: number) => Math.random() * (max - min) + min; const particles: Particle[] = []; const bursts = 5; const perBurst = 80; const spread = Math.PI / 3; const gravity = 0.25; const air = 0.002; const floorFriction = 0.96; for (let b = 0; b < bursts; b++) { const originX = (window.innerWidth / (bursts + 1)) * (b + 1); const originY = window.innerHeight * 0.15; const angleBase = -Math.PI / 2; for (let i = 0; i < perBurst; i++) { const angle = angleBase + rand(-spread, spread); const speed = rand(6, 12); particles.push({ x: originX + rand(-8, 8), y: originY + rand(-8, 8), w: rand(4, 8), h: rand(6, 14), vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, rot: rand(0, Math.PI * 2), rotSpeed: rand(-0.2, 0.2), tilt: rand(-0.8, 0.8), tiltSpeed: rand(-0.02, 0.02), color: colors[(Math.random() * colors.length) | 0] ?? '#ef4444', alpha: 1, decay: rand(0.004, 0.01), shape: Math.random() < 0.3 ? 'triangle' : 'rect', }); } } const tick = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); for (const p of particles) { // physics p.vy += gravity; p.vx *= 1 - air; p.vy *= 1 - air * 0.5; p.x += p.vx; p.y += p.vy; p.rot += p.rotSpeed; p.tilt += p.tiltSpeed; p.alpha -= p.decay; // floor bounce & friction const floor = window.innerHeight - 6; if (p.y > floor) { p.y = floor; p.vy *= -0.45; p.vx *= floorFriction; p.rotSpeed *= floorFriction; p.tiltSpeed *= floorFriction; } // draw if (p.alpha > 0) { ctx.globalAlpha = Math.max(0, p.alpha); ctx.save(); ctx.translate(p.x, p.y); ctx.rotate(p.rot + p.tilt); ctx.fillStyle = p.color; if (p.shape === 'rect') { ctx.fillRect(-p.w / 2, -p.h / 2, p.w, p.h); } else { ctx.beginPath(); ctx.moveTo(0, -p.h / 2); ctx.lineTo(p.w / 2, p.h / 2); ctx.lineTo(-p.w / 2, p.h / 2); ctx.closePath(); ctx.fill(); } ctx.restore(); } } const alive = particles.some((p) => p.alpha > 0.02); if (alive) { rafRef.current = requestAnimationFrame(tick); } else { // cleanup ctx.clearRect(0, 0, canvas.width, canvas.height); window.removeEventListener('resize', onResize); } }; tick(); }, []); const handleStart = () => { onStart?.(); if (isDraftLike) { setShowConfetti(true); } }; useEffect(() => { if (!showConfetti) return; runConfetti(); const t = setTimeout(() => { setShowConfetti(false); }, 2200); return () => { clearTimeout(t); if (rafRef.current) cancelAnimationFrame(rafRef.current); rafRef.current = null; }; }, [showConfetti, runConfetti]); return ( <> {/* Confetti Overlay */} {showConfetti && (
)} ); }