presentation/timers/surface.timer.ts
presentation/timers/surface.timer.ts is a file in Bane's Lab Site. 65 lines of code and 17 definitions.
import { STEP_LIMIT_MS } from "#configuration/constants/surface.constants";
import type { Segment } from "#types/surface.types";
export class SurfaceTimer {
private readonly segments: readonly Segment[];
private index = 0;
private elapsed = 0;
private begun = false;
private last: number | null = null;
private handle: number | null = null;
public constructor(segments: readonly Segment[]) {
this.segments = segments;
}
public run(): void {
if (this.handle !== null || this.segments.length === 0) {
return;
}
this.last = null;
this.request();
}
public halt(): void {
if (this.handle !== null) {
cancelAnimationFrame(this.handle);
this.handle = null;
}
}
public settle(count: number): void {
for (const segment of this.segments.slice(0, count)) {
segment.begin?.();
segment.advance?.(1);
}
this.index = count % this.segments.length;
this.elapsed = 0;
this.begun = false;
}
private request(): void {
this.handle = requestAnimationFrame((now) => {
this.tick(now);
});
}
private tick(now: number): void {
const step = this.last === null ? 0 : Math.min(now - this.last, STEP_LIMIT_MS);
this.last = now;
this.advanceBy(step);
this.request();
}
private current(): Segment | undefined {
return this.segments[this.index];
}
private advanceBy(step: number): void {
if (!this.begun) {
this.begun = true;
this.current()?.begin?.();
}
this.elapsed += step;
let segment = this.current();
while (segment !== undefined && this.elapsed >= segment.ms) {
segment.advance?.(1);
this.elapsed -= segment.ms;
this.index = (this.index + 1) % this.segments.length;
segment = this.current();
segment?.begin?.();
}
segment?.advance?.(this.elapsed / Math.max(segment.ms, 1));
}
}