diff options
Diffstat (limited to 'shared/components/src/utils/rafQueue.ts')
| -rw-r--r-- | shared/components/src/utils/rafQueue.ts | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/shared/components/src/utils/rafQueue.ts b/shared/components/src/utils/rafQueue.ts new file mode 100644 index 0000000..a56d9a7 --- /dev/null +++ b/shared/components/src/utils/rafQueue.ts @@ -0,0 +1,74 @@ +/** + * @name RequestAnimationFrameLimiter + * @description + * allows for multiple callbacks to be called + * within a single RAF function. + * It also spreads long running tasks across multiple + * microtask to help keep the main thread free for user interactions + * + */ +export class RequestAnimationFrameLimiter { + private queue: Array<(timestamp?: number) => void>; + private RAF_FN_LIMIT_MS: number; + private requestId: number | null; + constructor() { + this.queue = []; + // ideal limit for scroll based animations: https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution#reduce_complexity_or_use_web_workers + this.RAF_FN_LIMIT_MS = 3; + this.requestId = null; + } + + private flush(): void { + this.requestId = + this.queue.length === 0 + ? null + : window.requestAnimationFrame((timestamp) => { + const start = window.performance.now(); + let ellapsedTime = 0; + const { RAF_FN_LIMIT_MS } = this; + let count = 0; + + while ( + count < this.queue.length && + ellapsedTime < RAF_FN_LIMIT_MS + ) { + let item = this.queue[count]; + if (item) { + item(timestamp); + } + const finishTime = window.performance.now(); + + count = count + 1; + ellapsedTime = finishTime - start; + } + const newQueue = this.queue.slice(count); + + this.queue = newQueue; + this.flush(); + }); + } + public add(callback: () => void): void { + this.queue.push(callback); + if (this.requestId === null) { + this.flush(); + } + } +} + +let raf: RequestAnimationFrameLimiter | ServerSafeRAFLimiter = null; + +type ServerSafeRAFLimiter = { + add: (callback: () => void) => void; +}; + +export const getRafQueue = () => { + if (typeof window === 'undefined') { + // SSR safe + raf = { + add: (callback: () => void) => callback(), + }; + } else if (raf === null) { + raf = new RequestAnimationFrameLimiter(); + } + return raf; +}; |
