"use client" import { useCallback, useEffect, useRef } from "react" import { cn } from "@/lib/utils" const morphTime = 1.5 const cooldownTime = 0.5 const useMorphingText = (texts: string[]) => { const textIndexRef = useRef(0) const morphRef = useRef(0) const cooldownRef = useRef(0) const timeRef = useRef(new Date()) const text1Ref = useRef(null) const text2Ref = useRef(null) const setStyles = useCallback( (fraction: number) => { const [current1, current2] = [text1Ref.current, text2Ref.current] if (!current1 || !current2) return current2.style.filter = `blur(${Math.min(8 / fraction - 8, 100)}px)` current2.style.opacity = `${Math.pow(fraction, 0.4) * 100}%` const invertedFraction = 1 - fraction current1.style.filter = `blur(${Math.min( 8 / invertedFraction - 8, 100 )}px)` current1.style.opacity = `${Math.pow(invertedFraction, 0.4) * 100}%` current1.textContent = texts[textIndexRef.current % texts.length] current2.textContent = texts[(textIndexRef.current + 1) % texts.length] }, [texts] ) const doMorph = useCallback(() => { morphRef.current -= cooldownRef.current cooldownRef.current = 0 let fraction = morphRef.current / morphTime if (fraction > 1) { cooldownRef.current = cooldownTime fraction = 1 } setStyles(fraction) if (fraction === 1) { textIndexRef.current++ } }, [setStyles]) const doCooldown = useCallback(() => { morphRef.current = 0 const [current1, current2] = [text1Ref.current, text2Ref.current] if (current1 && current2) { current2.style.filter = "none" current2.style.opacity = "100%" current1.style.filter = "none" current1.style.opacity = "0%" } }, []) useEffect(() => { let animationFrameId: number const animate = () => { animationFrameId = requestAnimationFrame(animate) const newTime = new Date() const dt = (newTime.getTime() - timeRef.current.getTime()) / 1000 timeRef.current = newTime cooldownRef.current -= dt if (cooldownRef.current <= 0) doMorph() else doCooldown() } animate() return () => { cancelAnimationFrame(animationFrameId) } }, [doMorph, doCooldown]) return { text1Ref, text2Ref } } interface MorphingTextProps { className?: string texts: string[] } const Texts: React.FC> = ({ texts }) => { const { text1Ref, text2Ref } = useMorphingText(texts) return ( <> ) } const SvgFilters: React.FC = () => ( ) export const MorphingText: React.FC = ({ texts, className, }) => (
)