Abfahrtsanzeiger Display Basic Library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
libmonoformat/ts/src/driver.ts

95 lines
3.3 KiB

// ---------------------------------------------------------------------------
// MonoDisplayRenderer - tick-based canvas renderer
// ---------------------------------------------------------------------------
import { MonoDisplayParser } from "./parser";
import { MonoDisplayRenderer } from "./renderer";
/** Optional constructor arguments for MonoDisplayDriver */
export interface MonoDisplayDriverOptions {
/** CSS colour for on-pixels. Default "#ffffff" */
onColor?: string;
/** CSS colour for off-pixels / background. Default "#000000" */
offColor?: string;
/**
* Scale factor - each logical pixel → NxN canvas pixels.
* Keep at 1 and use CSS to stretch for crisp upscaling.
*/
scale?: number;
/** Default display width for elements without inherent size. Default 120. */
displayWidth?: number;
/** Default display height. Default 60. */
displayHeight?: number;
/**
* Render rate in ticks per second. All animations and scrolls are driven by this.
* Animation updateInterval, scroll speed, etc. are relative to this tick rate.
* Default 25.
*/
fps?: number;
/** Loop animations. Default true. */
loop?: boolean;
/** Error handler. Default: throw. */
onError?: (err: Error) => void;
/**
* Clock source for CurrentTime elements. Called once per render tick.
* Default: `() => new Date()` (system time).
* Override to freeze or mock the clock, e.g. `() => new Date('2024-01-01T12:00:00Z')`.
*/
now?: () => Date;
}
// ---------------------------------------------------------------------------
// MonoDisplayDriver - public API surface
// ---------------------------------------------------------------------------
/**
* Top-level driver. Attach to a canvas by ID, call load() with an async
* factory that returns a .bin ArrayBuffer.
*
* @example
* ```ts
* const driver = new MonoDisplayDriver("canvas_root", { fps: 25 });
* driver.load(() => loadBinFile("display.bin"));
* ```
*/
export class MonoDisplayDriver {
private canvas: HTMLCanvasElement;
private opts: Required<MonoDisplayDriverOptions>;
private parser: MonoDisplayParser;
private renderer: MonoDisplayRenderer | null = null;
constructor(canvasId: string, options: MonoDisplayDriverOptions = {}) {
const el = document.getElementById(canvasId);
if (!el || el.tagName !== "CANVAS") throw new Error(`#${canvasId} is not a <canvas>`);
this.canvas = el as HTMLCanvasElement;
this.opts = {
onColor: options.onColor ?? "#EC0",
offColor: options.offColor ?? "#000000",
scale: options.scale ?? 1,
displayWidth: options.displayWidth ?? 120,
displayHeight: options.displayHeight ?? 60,
fps: options.fps ?? 25,
loop: options.loop ?? true,
onError: options.onError ?? ((e: Error) => { throw e; }),
now: options.now ?? (() => new Date()),
};
this.parser = new MonoDisplayParser();
}
async load(loader: () => Promise<ArrayBuffer>): Promise<void> {
try {
const buffer = await loader();
const file = this.parser.parse(buffer);
if (!this.renderer) this.renderer = new MonoDisplayRenderer(this.canvas, this.opts);
this.renderer.render(file);
} catch (err) {
this.opts.onError(err instanceof Error ? err : new Error(String(err)));
}
}
stop(): void { this.renderer?.stop(); }
}