Ruleta actvidad completa en React
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import type { Categoria } from '../../../types'
|
||||
|
||||
interface RuletaSVGProps {
|
||||
categorias: Categoria[]
|
||||
rotacion: number
|
||||
isSpinning: boolean
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
export default function RuletaSVG({ categorias, rotacion, isSpinning, onClick }: RuletaSVGProps) {
|
||||
const cx = 200
|
||||
const cy = 200
|
||||
const r = 180
|
||||
const total = categorias.length
|
||||
const angleStep = 360 / total
|
||||
|
||||
function polarToCartesian(angle: number, radius: number) {
|
||||
const rad = (angle - 90) * (Math.PI / 180)
|
||||
return {
|
||||
x: cx + radius * Math.cos(rad),
|
||||
y: cy + radius * Math.sin(rad),
|
||||
}
|
||||
}
|
||||
|
||||
function sectorPath(startAngle: number, endAngle: number) {
|
||||
const start = polarToCartesian(startAngle, r)
|
||||
const end = polarToCartesian(endAngle, r)
|
||||
const largeArc = endAngle - startAngle > 180 ? 1 : 0
|
||||
return `M ${cx} ${cy} L ${start.x} ${start.y} A ${r} ${r} 0 ${largeArc} 1 ${end.x} ${end.y} Z`
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center justify-center">
|
||||
|
||||
{/* Indicador */}
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-2 z-10">
|
||||
<div className="w-0 h-0 border-l-[12px] border-r-[12px] border-t-[24px] border-l-transparent border-r-transparent border-t-gray-800 drop-shadow-md" />
|
||||
</div>
|
||||
|
||||
{/* Ruleta SVG */}
|
||||
<svg
|
||||
width="400"
|
||||
height="400"
|
||||
viewBox="0 0 400 400"
|
||||
onClick={!isSpinning ? onClick : undefined}
|
||||
className={`select-none drop-shadow-xl ${!isSpinning ? 'cursor-pointer' : 'cursor-not-allowed'}`}
|
||||
style={{
|
||||
transition: 'transform 4s cubic-bezier(0.25, 0.1, 0.25, 1)',
|
||||
transform: `rotate(${rotacion}deg)`,
|
||||
}}
|
||||
>
|
||||
{categorias.map((cat, i) => {
|
||||
const startAngle = i * angleStep
|
||||
const endAngle = startAngle + angleStep
|
||||
const midAngle = startAngle + angleStep / 2
|
||||
const textPos = polarToCartesian(midAngle, r * 0.62)
|
||||
|
||||
return (
|
||||
<g key={cat.id}>
|
||||
{/* Sector */}
|
||||
<path
|
||||
d={sectorPath(startAngle, endAngle)}
|
||||
fill={cat.color}
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
/>
|
||||
{/* Emoji */}
|
||||
<text
|
||||
x={textPos.x}
|
||||
y={textPos.y - 14}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
fontSize="28"
|
||||
>
|
||||
{cat.emoji}
|
||||
</text>
|
||||
{/* Nombre */}
|
||||
<text
|
||||
x={textPos.x}
|
||||
y={textPos.y + 16}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
fontSize="13"
|
||||
fontWeight="bold"
|
||||
fill="white"
|
||||
>
|
||||
{cat.nombre}
|
||||
</text>
|
||||
</g>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* Centro */}
|
||||
<circle cx={cx} cy={cy} r={22} fill="white" stroke="#e5e7eb" strokeWidth="3" />
|
||||
<circle cx={cx} cy={cy} r={10} fill="#7C3AED" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user