Skip to content

Instantly share code, notes, and snippets.

@tadeaspetak
Created June 16, 2020 15:55
Show Gist options
  • Save tadeaspetak/8cfeeed2a6bc768b51ad87ea8c2f6124 to your computer and use it in GitHub Desktop.
Save tadeaspetak/8cfeeed2a6bc768b51ad87ea8c2f6124 to your computer and use it in GitHub Desktop.
import { useEffect, useState, useRef } from "react";
export const useAnimateNumber = ({
start = 0,
end,
delay = 0,
duration = 0.5,
easing = (t) => t * (2 - t), // ease out quad
}: {
start?: number;
end: number;
delay?: number;
duration?: number;
easing?: (n: number) => number;
}) => {
const currentStart = useRef(start);
const beginningInMillis = useRef(0);
const animationFrame = useRef(0);
const [current, setCurrent] = useState(start);
const delayInMillis = delay * 1000;
const durationInMillis = duration * 1000;
const animate = () => {
const soFar = Math.max(new Date().getTime() - beginningInMillis.current, 0); // <0, durationInMillis>
const percentage = Math.min(easing(soFar / durationInMillis), 1); // <0, 1>
setCurrent(
Math.floor(
currentStart.current + percentage * (end - currentStart.current),
),
); // <start, end>
if (soFar < durationInMillis) {
animationFrame.current = requestAnimationFrame(animate);
} else {
setCurrent(Math.floor(end));
currentStart.current = end; // TODO: possibly make adjustable
}
};
useEffect(() => {
beginningInMillis.current = new Date().getTime() + delayInMillis;
const timeout = setTimeout(animate, delayInMillis);
return () => {
clearTimeout(timeout);
if (animationFrame.current > 0)
cancelAnimationFrame(animationFrame.current);
};
}, [end]);
return { current };
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment