"use client" import { useState, useRef, useCallback, useEffect } from "react" import { getStroke } from "perfect-freehand" interface TrazoLetraProps { letra: string color?: string onComplete?: () => void promptLevel?: number } export default function TrazoLetra({ letra, color = "#8CB8A0", onComplete, promptLevel = 0, }: TrazoLetraProps) { const canvasRef = useRef(null) const [isDrawing, setIsDrawing] = useState(false) const points = useRef([]) const [showLetter, setShowLetter] = useState(promptLevel < 2) useEffect(() => { if (promptLevel < 2) { drawLetterGuide(letra) } }, [letra, promptLevel]) useEffect(() => { const canvas = canvasRef.current if (!canvas) return canvas.style.touchAction = "none" }, []) const drawLetterGuide = (l: string) => { const canvas = canvasRef.current if (!canvas) return const ctx = canvas.getContext("2d") if (!ctx) return const w = canvas.width const h = canvas.height ctx.clearRect(0, 0, w, h) ctx.font = `${h * 0.6}px "Fredoka", sans-serif` ctx.textAlign = "center" ctx.textBaseline = "middle" ctx.globalAlpha = 0.15 ctx.fillStyle = "#2D3436" ctx.fillText(l.toUpperCase(), w / 2, h / 2) ctx.globalAlpha = 1 } const startDrawing = useCallback((e: React.PointerEvent) => { setIsDrawing(true) points.current = [[e.clientX, e.clientY, e.pressure || 0.5]] const canvas = canvasRef.current if (canvas) canvas.setPointerCapture(e.pointerId) }, []) const draw = useCallback((e: React.PointerEvent) => { if (!isDrawing) return points.current.push([e.clientX, e.clientY, e.pressure || 0.5]) renderStroke() }, [isDrawing]) const endDrawing = useCallback(() => { setIsDrawing(false) if (points.current.length > 5 && onComplete) { onComplete() } points.current = [] }, [onComplete]) const renderStroke = () => { const canvas = canvasRef.current if (!canvas) return const ctx = canvas.getContext("2d") if (!ctx) return const stroke = getStroke(points.current, { size: 12, thinning: 0.5, smoothing: 0.5, streamline: 0.5, }) if (stroke.length < 2) return ctx.clearRect(0, 0, canvas.width, canvas.height) if (showLetter) drawLetterGuide(letra) ctx.beginPath() ctx.moveTo(stroke[0][0], stroke[0][1]) for (let i = 1; i < stroke.length; i++) { ctx.lineTo(stroke[i][0], stroke[i][1]) } ctx.strokeStyle = color ctx.lineWidth = 3 ctx.lineCap = "round" ctx.lineJoin = "round" ctx.fillStyle = color ctx.fill() if (promptLevel < 3) { drawDirectionArrow(ctx, letra) } } const drawDirectionArrow = (ctx: CanvasRenderingContext2D, l: string) => { const w = ctx.canvas.width const h = ctx.canvas.height ctx.save() ctx.globalAlpha = 0.3 ctx.strokeStyle = "#8CB8A0" ctx.lineWidth = 2 ctx.fillStyle = "#8CB8A0" const arrowPaths: Record = { a: { sx: w * 0.3, sy: h * 0.7, ex: w * 0.7, ey: h * 0.3 }, e: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, i: { sx: w * 0.5, sy: h * 0.2, ex: w * 0.5, ey: h * 0.8 }, o: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.6, ey: h * 0.6 }, u: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.5 }, // Consonants: generic top-to-bottom, left-to-right arrow b: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, c: { sx: w * 0.6, sy: h * 0.3, ex: w * 0.4, ey: h * 0.7 }, d: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, f: { sx: w * 0.5, sy: h * 0.2, ex: w * 0.5, ey: h * 0.8 }, g: { sx: w * 0.6, sy: h * 0.3, ex: w * 0.4, ey: h * 0.7 }, h: { sx: w * 0.5, sy: h * 0.2, ex: w * 0.5, ey: h * 0.8 }, j: { sx: w * 0.5, sy: h * 0.2, ex: w * 0.5, ey: h * 0.8 }, k: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, l: { sx: w * 0.5, sy: h * 0.2, ex: w * 0.5, ey: h * 0.8 }, m: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.5 }, n: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.5 }, p: { sx: w * 0.5, sy: h * 0.2, ex: w * 0.5, ey: h * 0.8 }, q: { sx: w * 0.6, sy: h * 0.3, ex: w * 0.4, ey: h * 0.7 }, r: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, s: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, t: { sx: w * 0.5, sy: h * 0.2, ex: w * 0.5, ey: h * 0.8 }, v: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, w: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.5 }, x: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, y: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.7 }, z: { sx: w * 0.3, sy: h * 0.3, ex: w * 0.7, ey: h * 0.5 }, } const path = arrowPaths[l.toLowerCase()] if (path) { ctx.beginPath() ctx.moveTo(path.sx, path.sy) ctx.lineTo(path.ex, path.ey) ctx.stroke() const angle = Math.atan2(path.ey - path.sy, path.ex - path.sx) const sz = 6 ctx.beginPath() ctx.moveTo(path.ex, path.ey) ctx.lineTo(path.ex - sz * Math.cos(angle - Math.PI / 6), path.ey - sz * Math.sin(angle - Math.PI / 6)) ctx.lineTo(path.ex - sz * Math.cos(angle + Math.PI / 6), path.ey - sz * Math.sin(angle + Math.PI / 6)) ctx.closePath() ctx.fill() } ctx.restore() } return ( ) }