"use client"; import React, { useState, useRef, useEffect } from "react"; export interface QuestionWheelProps { questions: string[]; soundUrl?: string; colors?: string[]; } const DEFAULT_COLORS = [ "#3369e8", // Blue "#d50f25", // Red "#eeb211", // Yellow "#009925", // Green "#ff6600", // Orange "#9933cc", // Purple "#00ccff", // Cyan "#ff3399", // Pink ]; export function SpinningPickerWheel({ questions, soundUrl, colors = DEFAULT_COLORS }: QuestionWheelProps) { const [isSpinning, setIsSpinning] = useState(false); const [selectedQuestion, setSelectedQuestion] = useState(null); const [isWaitingForModal, setIsWaitingForModal] = useState(false); const [isClosing, setIsClosing] = useState(false); const [isMounted, setIsMounted] = useState(false); const [isSoundEnabled, setIsSoundEnabled] = useState(true); const wheelRef = useRef(null); const idleRotationRef = useRef(0); const audioRef = useRef(null); const animationFrameRef = useRef(undefined); const safeColors = colors.length > 0 ? colors : DEFAULT_COLORS; const displayQuestions = questions.slice(0, 24); const numSegments = displayQuestions.length; if (numSegments === 0) { return
No questions provided
; } const anglePerSegment = 360 / numSegments; useEffect(() => { if (soundUrl) { audioRef.current = new Audio(soundUrl); audioRef.current.loop = false; } const timer = setTimeout(() => setIsMounted(true), 0); return () => clearTimeout(timer); }, [soundUrl]); useEffect(() => { if (!isMounted || isSpinning || selectedQuestion || isWaitingForModal) return; const rotateIdle = () => { idleRotationRef.current += 0.1; if (wheelRef.current) { wheelRef.current.style.transform = `rotate(${idleRotationRef.current}deg)`; } animationFrameRef.current = requestAnimationFrame(rotateIdle); }; animationFrameRef.current = requestAnimationFrame(rotateIdle); return () => { if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); } }; }, [isMounted, isSpinning, selectedQuestion, isWaitingForModal]); const spinWheel = () => { if (isSpinning || selectedQuestion || isWaitingForModal) return; setIsSpinning(true); setSelectedQuestion(null); if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); } if (isSoundEnabled && audioRef.current) { audioRef.current.currentTime = 0; audioRef.current.play().catch(e => console.log("Audio play failed:", e)); } const winnerIndex = Math.floor(Math.random() * numSegments); const extraSpins = 5; const baseSpin = 360 * extraSpins; const segmentCenter = (winnerIndex * anglePerSegment) + (anglePerSegment / 2); const currentRotation = idleRotationRef.current; const targetAngle = (180 - segmentCenter); const normalizedTargetAngle = ((targetAngle % 360) + 360) % 360; const currentAngle = currentRotation % 360; const normalizedCurrentAngle = ((currentAngle % 360) + 360) % 360; let rotationNeeded = normalizedTargetAngle - normalizedCurrentAngle; if (rotationNeeded < 0) { rotationNeeded += 360; } const newRotation = currentRotation + rotationNeeded + baseSpin; idleRotationRef.current = newRotation; if (wheelRef.current) { wheelRef.current.style.transition = "transform 2.5s cubic-bezier(0.25, 0.1, 0.25, 1)"; wheelRef.current.style.transform = `rotate(${newRotation}deg)`; } setTimeout(() => { setIsSpinning(false); if (wheelRef.current) { wheelRef.current.style.transition = "none"; } if (audioRef.current) { audioRef.current.pause(); audioRef.current.currentTime = 0; } setIsWaitingForModal(true); setTimeout(() => { setSelectedQuestion(displayQuestions[winnerIndex]); setIsWaitingForModal(false); }, 1000); }, 2700); }; const closeModal = () => { setIsClosing(true); setTimeout(() => { setSelectedQuestion(null); setIsClosing(false); }, 300); }; const getSegmentPath = (index: number) => { const startAngle = index * anglePerSegment; const endAngle = (index + 1) * anglePerSegment; const startRad = (Math.PI / 180) * startAngle; const endRad = (Math.PI / 180) * endAngle; const radius = 200; const center = 200; const x1 = center + radius * Math.cos(startRad); const y1 = center + radius * Math.sin(startRad); const x2 = center + radius * Math.cos(endRad); const y2 = center + radius * Math.sin(endRad); return `M ${center} ${center} L ${x1} ${y1} A ${radius} ${radius} 0 0 1 ${x2} ${y2} Z`; }; return (
{isMounted && displayQuestions.map((q, i) => ( {q.slice(0, 22) + (q.length > 22 ? "..." : "")} ))}
{selectedQuestion && (
e.stopPropagation()} >

The Question Is:

{selectedQuestion}

)}
); }