Web frontend
This commit is contained in:
33
web/src/hooks/usePlaybackClock.ts
Normal file
33
web/src/hooks/usePlaybackClock.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/** A smooth playback position, from a position that only arrives twice a second.
|
||||
|
||||
Driving a progress bar straight off the websocket visibly steps. This interpolates
|
||||
between frames on every animation frame and re-seeds whenever a real one lands, which
|
||||
also gives beat-synced animation the frame-accurate clock it will need later.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
export function usePlaybackClock(position: number, playing: boolean): number {
|
||||
const [interpolated, setInterpolated] = useState(position);
|
||||
const anchor = useRef({ position, at: performance.now() });
|
||||
|
||||
// A new server frame is the truth; restart the interpolation from it.
|
||||
useEffect(() => {
|
||||
anchor.current = { position, at: performance.now() };
|
||||
setInterpolated(position);
|
||||
}, [position]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!playing) return;
|
||||
let frame = 0;
|
||||
const tick = () => {
|
||||
const { position: base, at } = anchor.current;
|
||||
setInterpolated(base + (performance.now() - at) / 1000);
|
||||
frame = requestAnimationFrame(tick);
|
||||
};
|
||||
frame = requestAnimationFrame(tick);
|
||||
return () => cancelAnimationFrame(frame);
|
||||
}, [playing]);
|
||||
|
||||
return playing ? interpolated : position;
|
||||
}
|
||||
Reference in New Issue
Block a user