use-animation-frame
v1.0.0
Published
A React hook to run requestAnimationFrame seamlessly
Maintainers
Readme
use-animation-frame
A hook to effortlessly run requestAnimationFrame() in React (demo):
import useAnimationFrame from 'use-animation-frame';
const Counter = () => {
const [time, setTime] = useState(0);
useAnimationFrame(e => setTime(e.time));
return <div>Running for:<br/>{time.toFixed(1)}s</div>;
};Inspired by CSS-Tricks' Using requestAnimationFrame with React Hooks and my twitter reply.
API
useAnimationFrame(callback);Calls callback on every animation frame. The callback receives an object with two properties (based on the performance.now() API):
time: seconds elapsed since the hook was first mounted. Useful for driving animations tied to a wall clock.delta: seconds elapsed since the last frame. Useful for frame-rate-independent movement; e.g.1 / e.deltagives the current FPS.
All times are in seconds (including decimals).
The callback is stored in a ref, so it always reflects the latest closure — state and props are always up to date without restarting the animation loop. No dependency array needed.
// TypeScript: the callback type is inferred automatically
useAnimationFrame(({ time, delta }: { time: number; delta: number }) => {
// ...
});Example: moving a value
Use delta to advance a value independent of frame rate:
import { useState } from 'react';
import useAnimationFrame from 'use-animation-frame';
const Progress = () => {
const [x, setX] = useState(0);
useAnimationFrame(({ delta }) => setX(prev => (prev + delta * 100) % 100));
return <div style={{ marginLeft: `${x}%` }}>→</div>;
};Example: FPS counter
With my other library use-interpolation it's easy to smooth the FPS reading (see in CodeSandbox):
import { useState } from "react";
import useInterpolation from 'use-interpolation';
import useAnimationFrame from 'use-animation-frame';
const Counter = () => {
const [time, setTime] = useState(0);
const [fps, setFps] = useInterpolation(1000); // 1s smoothing window
useAnimationFrame(({ time, delta }) => {
setFps(1 / delta);
setTime(time);
});
return (
<div>
{time.toFixed(1)}s
<br />
{fps && Math.floor(fps.value)} FPS
</div>
);
};