this repo has no description
1/**
2 * @name RequestAnimationFrameLimiter
3 * @description
4 * allows for multiple callbacks to be called
5 * within a single RAF function.
6 * It also spreads long running tasks across multiple
7 * microtask to help keep the main thread free for user interactions
8 *
9 */
10export class RequestAnimationFrameLimiter {
11 private queue: Array<(timestamp?: number) => void>;
12 private RAF_FN_LIMIT_MS: number;
13 private requestId: number | null;
14 constructor() {
15 this.queue = [];
16 // ideal limit for scroll based animations: https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution#reduce_complexity_or_use_web_workers
17 this.RAF_FN_LIMIT_MS = 3;
18 this.requestId = null;
19 }
20
21 private flush(): void {
22 this.requestId =
23 this.queue.length === 0
24 ? null
25 : window.requestAnimationFrame((timestamp) => {
26 const start = window.performance.now();
27 let ellapsedTime = 0;
28 const { RAF_FN_LIMIT_MS } = this;
29 let count = 0;
30
31 while (
32 count < this.queue.length &&
33 ellapsedTime < RAF_FN_LIMIT_MS
34 ) {
35 let item = this.queue[count];
36 if (item) {
37 item(timestamp);
38 }
39 const finishTime = window.performance.now();
40
41 count = count + 1;
42 ellapsedTime = finishTime - start;
43 }
44 const newQueue = this.queue.slice(count);
45
46 this.queue = newQueue;
47 this.flush();
48 });
49 }
50 public add(callback: () => void): void {
51 this.queue.push(callback);
52 if (this.requestId === null) {
53 this.flush();
54 }
55 }
56}
57
58let raf: RequestAnimationFrameLimiter | ServerSafeRAFLimiter = null;
59
60type ServerSafeRAFLimiter = {
61 add: (callback: () => void) => void;
62};
63
64export const getRafQueue = () => {
65 if (typeof window === 'undefined') {
66 // SSR safe
67 raf = {
68 add: (callback: () => void) => callback(),
69 };
70 } else if (raf === null) {
71 raf = new RequestAnimationFrameLimiter();
72 }
73 return raf;
74};