import { useState, useCallback, useRef } from 'react'; const MATRIX_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%&*!?<>[]{}アイウエオカキクケコサシスセソタチツテトナニヌネノ'; interface GlitchTextProps { text: string; className?: string; glitchOnHover?: boolean; as?: 'span' | 'h1' | 'h2' | 'h3' | 'h4' | 'p'; } const GlitchText = ({ text, className = '', glitchOnHover = true, as: Component = 'span' }: GlitchTextProps) => { const [displayText, setDisplayText] = useState(text); const [isGlitching, setIsGlitching] = useState(false); const intervalRef = useRef(null); const iterationRef = useRef(0); const getRandomChar = () => { return MATRIX_CHARS[Math.floor(Math.random() * MATRIX_CHARS.length)]; }; const startGlitch = useCallback(() => { if (!glitchOnHover || isGlitching) return; setIsGlitching(true); iterationRef.current = 0; intervalRef.current = setInterval(() => { setDisplayText(prev => { return text .split('') .map((char, index) => { if (char === ' ') return ' '; // Gradually resolve characters from left to right if (index < iterationRef.current) { return text[index]; } // Random chance to show original or glitch return Math.random() > 0.5 ? getRandomChar() : char; }) .join(''); }); iterationRef.current += 1; // Complete after all characters resolved if (iterationRef.current > text.length) { if (intervalRef.current) { clearInterval(intervalRef.current); } setDisplayText(text); setIsGlitching(false); } }, 50); }, [text, glitchOnHover, isGlitching]); const stopGlitch = useCallback(() => { if (intervalRef.current) { clearInterval(intervalRef.current); } setDisplayText(text); setIsGlitching(false); }, [text]); return ( {displayText} ); }; export default GlitchText;