diff --git a/src/components/cipher.tsx b/src/components/cipher.tsx new file mode 100644 index 0000000..8e705a9 --- /dev/null +++ b/src/components/cipher.tsx @@ -0,0 +1,59 @@ +import { useState, useEffect } from 'react'; + +interface CipherTextProps { + interval?: number; + text: string; +} + +const chars = '-_~`!@#$%^&*()+=[]{}|;:,.<>?'; + +export function CipherText({ interval = 50, text }: CipherTextProps) { + const [outputText, setOutputText] = useState(''); + const [isMounted, setIsMounted] = useState(false); + + useEffect(() => { + setIsMounted(true); + }, []); + + useEffect(() => { + let timer: NodeJS.Timeout; + + if (outputText !== text) { + timer = setInterval(() => { + if (outputText.length < text.length) { + setOutputText(prev => prev + text[prev.length]); + } else { + clearInterval(timer); + } + }, interval); + } + + return () => clearInterval(timer); + }, [text, interval, outputText]); + + useEffect(() => { + if (outputText === text) { + setTimeout(() => setOutputText(''), 6000); + } + }, [outputText, text]); + + const remainder = + outputText.length < text.length + ? text + .slice(outputText.length) + .split('') + .map(() => chars[Math.floor(Math.random() * chars.length)]) + .join('') + : ''; + + if (!isMounted) { + return {text}; + } + + return ( + + {outputText} + {remainder} + + ); +} diff --git a/src/components/hero.astro b/src/components/hero.astro index c596a71..9621f36 100644 --- a/src/components/hero.astro +++ b/src/components/hero.astro @@ -2,6 +2,7 @@ import { BsSoundwave } from 'react-icons/bs/index'; import { Container } from './container'; +import { CipherText } from './cipher'; import { count as soundCount } from '@/lib/sounds'; @@ -24,7 +25,9 @@ const count = soundCount();