From 6ec259a0e71174651bae95d4628138bf6fd68742 Mon Sep 17 00:00:00 2001 From: kj_sh604 Date: Sun, 15 Mar 2026 16:19:35 -0400 Subject: refactor: packages/ --- packages/excalidraw/animation-frame-handler.ts | 79 ++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 packages/excalidraw/animation-frame-handler.ts (limited to 'packages/excalidraw/animation-frame-handler.ts') diff --git a/packages/excalidraw/animation-frame-handler.ts b/packages/excalidraw/animation-frame-handler.ts new file mode 100644 index 0000000..b1a9844 --- /dev/null +++ b/packages/excalidraw/animation-frame-handler.ts @@ -0,0 +1,79 @@ +export type AnimationCallback = (timestamp: number) => void | boolean; + +export type AnimationTarget = { + callback: AnimationCallback; + stopped: boolean; +}; + +export class AnimationFrameHandler { + private targets = new WeakMap(); + private rafIds = new WeakMap(); + + register(key: object, callback: AnimationCallback) { + this.targets.set(key, { callback, stopped: true }); + } + + start(key: object) { + const target = this.targets.get(key); + + if (!target) { + return; + } + + if (this.rafIds.has(key)) { + return; + } + + this.targets.set(key, { ...target, stopped: false }); + this.scheduleFrame(key); + } + + stop(key: object) { + const target = this.targets.get(key); + if (target && !target.stopped) { + this.targets.set(key, { ...target, stopped: true }); + } + + this.cancelFrame(key); + } + + private constructFrame(key: object): FrameRequestCallback { + return (timestamp: number) => { + const target = this.targets.get(key); + + if (!target) { + return; + } + + const shouldAbort = this.onFrame(target, timestamp); + + if (!target.stopped && !shouldAbort) { + this.scheduleFrame(key); + } else { + this.cancelFrame(key); + } + }; + } + + private scheduleFrame(key: object) { + const rafId = requestAnimationFrame(this.constructFrame(key)); + + this.rafIds.set(key, rafId); + } + + private cancelFrame(key: object) { + if (this.rafIds.has(key)) { + const rafId = this.rafIds.get(key)!; + + cancelAnimationFrame(rafId); + } + + this.rafIds.delete(key); + } + + private onFrame(target: AnimationTarget, timestamp: number): boolean { + const shouldAbort = target.callback(timestamp); + + return shouldAbort ?? false; + } +} -- cgit v1.2.3