UNPKG

@gfazioli/mantine-text-animate

Version:

The TextAnimate component allows you to animate text with various effects.

111 lines (108 loc) 3.15 kB
'use client'; import { useState, useRef, useCallback, useEffect } from 'react'; function useRotatingText(options) { const { values, animate = true, interval = 3e3, onCycle } = options; const { speed = 1 } = options; const [currentIndex, setCurrentIndex] = useState(0); const [isTransitioning, setIsTransitioning] = useState(false); const [isActive, setIsActive] = useState(animate); const timerRef = useRef(null); const fallbackTimerRef = useRef(null); const onCycleRef = useRef(onCycle); onCycleRef.current = onCycle; const nextIndex = (currentIndex + 1) % values.length; const safeSpeed = Math.max(0.1, speed || 1); const transitionDuration = 500 / safeSpeed; const completeTransition = useCallback(() => { if (fallbackTimerRef.current) { clearTimeout(fallbackTimerRef.current); fallbackTimerRef.current = null; } setCurrentIndex((prev) => { const next = (prev + 1) % values.length; onCycleRef.current?.(next); return next; }); setIsTransitioning(false); }, [values.length]); useEffect(() => { if (!isActive || values.length <= 1 || isTransitioning) { return; } timerRef.current = setTimeout(() => { setIsTransitioning(true); }, interval); return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, [isActive, currentIndex, interval, values.length, isTransitioning]); useEffect(() => { if (!isTransitioning) { return; } fallbackTimerRef.current = setTimeout(completeTransition, transitionDuration + 50); return () => { if (fallbackTimerRef.current) { clearTimeout(fallbackTimerRef.current); } }; }, [isTransitioning, transitionDuration, completeTransition]); const prevAnimateRef = useRef(animate); useEffect(() => { if (animate && !prevAnimateRef.current) { setIsActive(true); } if (!animate && prevAnimateRef.current) { if (timerRef.current) { clearTimeout(timerRef.current); } setIsActive(false); setIsTransitioning(false); } prevAnimateRef.current = animate; }, [animate]); useEffect(() => { return () => { if (timerRef.current) { clearTimeout(timerRef.current); } if (fallbackTimerRef.current) { clearTimeout(fallbackTimerRef.current); } }; }, []); const onTransitionEnd = useCallback(() => { completeTransition(); }, [completeTransition]); const start = useCallback(() => { setIsActive(true); }, []); const stop = useCallback(() => { if (timerRef.current) { clearTimeout(timerRef.current); } setIsActive(false); }, []); const reset = useCallback(() => { if (timerRef.current) { clearTimeout(timerRef.current); } setCurrentIndex(0); setIsTransitioning(false); }, []); return { currentIndex, currentText: values[currentIndex] ?? "", nextIndex, nextText: values[nextIndex] ?? "", isTransitioning, onTransitionEnd, start, stop, reset }; } export { useRotatingText }; //# sourceMappingURL=use-rotating-text.mjs.map