import { throttleToNextFrame as _throttleToNextFrame, bind } from '@tldraw/utils' import type { Editor } from '../../Editor' const throttleToNextFrame = typeof process !== 'undefined' && process.env.NODE_ENV === 'test' ? // At test time we should use actual raf and not throttle, because throttle was set up to evaluate immediately during tests, which causes stack overflow // for the tick manager since it sets up a raf loop. function mockThrottle(cb: any) { // eslint-disable-next-line no-restricted-globals const frame = requestAnimationFrame(cb) return () => cancelAnimationFrame(frame) } : _throttleToNextFrame /** @internal */ export class TickManager { constructor(public editor: Editor) { this.editor.disposables.add(this.dispose) this.start() } cancelRaf?: null | (() => void) isPaused = true now = 0 start() { this.isPaused = false this.cancelRaf?.() this.cancelRaf = throttleToNextFrame(this.tick) this.now = Date.now() } @bind tick() { if (this.isPaused) { return } const now = Date.now() const elapsed = now - this.now this.now = now this.editor.inputs.updatePointerVelocity(elapsed) this.editor.emit('frame', elapsed) this.editor.emit('tick', elapsed) this.cancelRaf = throttleToNextFrame(this.tick) } // Clear the listener @bind dispose() { this.isPaused = true this.cancelRaf?.() } }