@gfazioli/mantine-text-animate
Version:
The TextAnimate component allows you to animate text with various effects.
111 lines (108 loc) • 3.15 kB
JavaScript
'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