UNPKG

@supunlakmal/hooks

Version:

A collection of reusable React hooks

77 lines 3.28 kB
import { useRef, useCallback, useState, useEffect } from 'react'; /** * Custom hook to manage a basic animation loop using requestAnimationFrame. * * @param callback - Function called on each animation frame. Receives progress (0-1) and elapsed time (ms). * @param options - Configuration object. * @param options.duration - Total duration of the animation in milliseconds. * @param options.onComplete - Optional callback triggered when the animation completes its duration. * @returns Controls object with start, stop, reset functions and running status. */ export const useAnimation = (callback, { duration, onComplete }) => { const [isRunning, setIsRunning] = useState(false); const frameIdRef = useRef(null); const startTimeRef = useRef(null); const callbackRef = useRef(callback); const onCompleteRef = useRef(onComplete); // Update refs if callbacks change callbackRef.current = callback; onCompleteRef.current = onComplete; const loop = useCallback((currentTime) => { var _a; if (startTimeRef.current === null) { startTimeRef.current = currentTime; // Set start time on first frame } const elapsed = currentTime - startTimeRef.current; const progress = Math.min(1, elapsed / duration); // Clamp progress between 0 and 1 // Execute the user's callback callbackRef.current(progress, elapsed); if (progress < 1) { // Continue the loop frameIdRef.current = requestAnimationFrame(loop); } else { // Animation finished setIsRunning(false); startTimeRef.current = null; frameIdRef.current = null; (_a = onCompleteRef.current) === null || _a === void 0 ? void 0 : _a.call(onCompleteRef); // Call completion callback if provided } }, [duration]); const start = useCallback(() => { if (isRunning || typeof window === 'undefined') return; // Prevent starting if already running or SSR setIsRunning(true); startTimeRef.current = null; // Reset start time frameIdRef.current = requestAnimationFrame(loop); }, [isRunning, loop]); const stop = useCallback(() => { if (!isRunning || frameIdRef.current === null) return; cancelAnimationFrame(frameIdRef.current); setIsRunning(false); // Keep startTimeRef as is, allows potential resume? // For a simple stop, maybe resetting startTimeRef isn't needed. frameIdRef.current = null; }, [isRunning]); const reset = useCallback(() => { if (frameIdRef.current !== null) { cancelAnimationFrame(frameIdRef.current); } setIsRunning(false); startTimeRef.current = null; frameIdRef.current = null; // Optionally call callback with initial state (progress 0)? // callbackRef.current(0, 0); }, []); // Ensure animation is cancelled on unmount useEffect(() => { return () => { if (frameIdRef.current !== null) { cancelAnimationFrame(frameIdRef.current); } }; }, []); return { start, stop, reset, isRunning }; }; //# sourceMappingURL=useAnimation.js.map