useTicker
A hook for creating performant paint-dependent loops using requestAnimationFrame with precise timing and delta calculations
Source Code
import { useCallback, useEffect, useMemo, useRef } from "react";
type Ticker = {
start: () => void;
stop: () => void;
paused: boolean;
};
type TickerCallback = (timestamp: number, delta: number) => void | boolean;
const MIN_DELTA = 0.000000001;
export const useTicker = (callback: TickerCallback): Ticker => {
const rafId = useRef<number | null>(null);
const previousTimestamp = useRef(0);
const callbackRef = useRef(callback);
const isRunning = useRef(false);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
const tick = useCallback((timestamp: number) => {
if (!isRunning.current) return;
const delta = Math.max(MIN_DELTA, timestamp - previousTimestamp.current);
const shouldContinue = callbackRef.current(timestamp, delta);
previousTimestamp.current = timestamp;
if (shouldContinue !== false) {
rafId.current = window.requestAnimationFrame(tick);
} else {
isRunning.current = false;
rafId.current = null;
}
}, []);
const start = useCallback(() => {
if (rafId.current) {
window.cancelAnimationFrame(rafId.current);
}
isRunning.current = true;
previousTimestamp.current = performance.now();
rafId.current = window.requestAnimationFrame(tick);
}, [tick]);
const stop = useCallback(() => {
if (rafId.current) {
window.cancelAnimationFrame(rafId.current);
rafId.current = null;
}
isRunning.current = false;
}, []);
useEffect(() => {
return () => {
stop();
};
}, [stop]);
return useMemo(
() => ({
start,
stop,
get paused() {
return !isRunning.current;
},
}),
[start, stop]
);
};
API Reference
Prop | Default | Type | Description |
---|---|---|---|
| - |
| The callback to call on each tick. |
The hook returns an object with the following properties:
start
: A function to start the ticker.stop
: A function to stop the ticker.paused
: A boolean indicating if the ticker is paused.
Examples
Looping Animation
About requestAnimationFrame
requestAnimationFrame
is a browser API that schedules a callback to be executed before the next browser repaint. It's the recommended way to create animations and game loops as it automatically synchronizes with the browser's refresh rate.
You can read a more in-depth overview of requestAnimationFrame
on our blog: Mastering JavaScript Web Animations.