46 lines
1.3 KiB
TypeScript
46 lines
1.3 KiB
TypeScript
import { useState, useEffect, useCallback, useRef } from "react";
|
|||
|
|
|
||
|
|
export function useTimer() {
|
||
|
|
const [elapsed, setElapsed] = useState(0);
|
||
|
|
const startTimeRef = useRef<number | null>(null);
|
||
|
|
const rafRef = useRef<number | null>(null);
|
||
|
|
|
||
|
|
const startRef = useRef(() => {
|
||
|
|
startTimeRef.current = performance.now();
|
||
|
|
function tick() {
|
||
|
|
if (startTimeRef.current !== null) {
|
||
|
|
setElapsed((performance.now() - startTimeRef.current) / 1000);
|
||
|
|
rafRef.current = requestAnimationFrame(tick);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
rafRef.current = requestAnimationFrame(tick);
|
||
|
|
});
|
||
|
|
|
||
|
|
const stopRef = useRef((): number => {
|
||
|
|
let final = 0;
|
||
|
|
if (startTimeRef.current !== null) {
|
||
|
|
final = (performance.now() - startTimeRef.current) / 1000;
|
||
|
|
setElapsed(final);
|
||
|
|
}
|
||
|
|
if (rafRef.current !== null) {
|
||
|
|
cancelAnimationFrame(rafRef.current);
|
||
|
|
rafRef.current = null;
|
||
|
|
}
|
||
|
|
startTimeRef.current = null;
|
||
|
|
return final;
|
||
|
|
});
|
||
|
|
|
||
|
|
const resetRef = useRef(() => {
|
||
|
|
stopRef.current();
|
||
|
|
setElapsed(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
const start = useCallback(() => startRef.current(), []);
|
||
|
|
const stop = useCallback(() => stopRef.current(), []);
|
||
|
|
const reset = useCallback(() => resetRef.current(), []);
|
||
|
|
|
||
|
|
useEffect(() => () => { if (rafRef.current !== null) cancelAnimationFrame(rafRef.current); }, []);
|
||
|
|
|
||
|
|
return { elapsed, start, stop, reset };
|
||
|
|
}
|