blob: a56d9a7f17739cde2873e229eb713694ae3f7bb8 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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;
};
|