49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
/** Small scheduler shared by the two canvas treatments. Visibility and business
|
|
* state stay outside it; stopping an animation never stops a task. */
|
|
export function createParticleLoop({
|
|
draw, canRender, canAnimate,
|
|
requestFrame = requestAnimationFrame,
|
|
cancelFrame = cancelAnimationFrame,
|
|
}: {
|
|
draw: (elapsed: number, still: boolean) => void;
|
|
canRender: () => boolean;
|
|
canAnimate: () => boolean;
|
|
requestFrame?: (callback: FrameRequestCallback) => number;
|
|
cancelFrame?: (handle: number) => void;
|
|
}) {
|
|
let frame: number | null = null;
|
|
let previous: number | null = null;
|
|
let elapsed = 0;
|
|
let disposed = false;
|
|
|
|
function tick(now: number): void {
|
|
frame = null;
|
|
if (disposed || !canRender()) return;
|
|
if (!canAnimate()) { draw(elapsed, true); return; }
|
|
if (previous === null || now - previous >= 1000 / 30) {
|
|
elapsed += previous === null ? 0 : Math.min(now - previous, 75);
|
|
previous = now;
|
|
draw(elapsed, false);
|
|
}
|
|
frame = requestFrame(tick);
|
|
}
|
|
|
|
function sync(): void {
|
|
if (disposed) return;
|
|
if (frame !== null) cancelFrame(frame);
|
|
frame = null;
|
|
previous = null;
|
|
if (!canRender()) return;
|
|
draw(elapsed, !canAnimate());
|
|
if (canAnimate()) frame = requestFrame(tick);
|
|
}
|
|
|
|
function dispose(): void {
|
|
disposed = true;
|
|
if (frame !== null) cancelFrame(frame);
|
|
frame = null;
|
|
}
|
|
|
|
return { sync, dispose };
|
|
}
|