diff options
Diffstat (limited to 'makima/frontend/src/components/JapaneseHoverText.tsx')
| -rw-r--r-- | makima/frontend/src/components/JapaneseHoverText.tsx | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/makima/frontend/src/components/JapaneseHoverText.tsx b/makima/frontend/src/components/JapaneseHoverText.tsx new file mode 100644 index 0000000..3e60ee2 --- /dev/null +++ b/makima/frontend/src/components/JapaneseHoverText.tsx @@ -0,0 +1,77 @@ +import { useState, useCallback, useRef } from "react"; + +const GLYPHS = "▒▓░█#@*+:-/[]{}<>_"; + +interface JapaneseHoverTextProps { + japanese: string; + english: string; + className?: string; +} + +/** + * Displays Japanese text, transitions to English on hover with scramble effect + */ +export function JapaneseHoverText({ + japanese, + english, + className = "", +}: JapaneseHoverTextProps) { + const [isHovered, setIsHovered] = useState(false); + const [displayText, setDisplayText] = useState(english); + const timerRef = useRef<ReturnType<typeof setInterval> | null>(null); + const iterationRef = useRef(0); + + const scrambleToEnglish = useCallback(() => { + setIsHovered(true); + + // Clear any existing animation + if (timerRef.current) { + clearInterval(timerRef.current); + } + + iterationRef.current = 0; + + timerRef.current = setInterval(() => { + const text = english; + const iteration = iterationRef.current; + + const display = text + .split("") + .map((char, index) => { + if (index < iteration) return char; + return GLYPHS.charAt(Math.floor(Math.random() * GLYPHS.length)); + }) + .join(""); + + setDisplayText(display); + iterationRef.current += 1; + + if (iteration > text.length + 2) { + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + } + setDisplayText(english); + } + }, 26); + }, [english]); + + const resetToJapanese = useCallback(() => { + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + } + setIsHovered(false); + setDisplayText(english); + }, [english]); + + return ( + <span + className={`cursor-default ${className}`} + onMouseEnter={scrambleToEnglish} + onMouseLeave={resetToJapanese} + > + {isHovered ? displayText : japanese} + </span> + ); +} |
