@lytjs/common-raf 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +68 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.mjs +62 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
- package/src/index.ts +97 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
var _raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(() => cb(performance.now()), 16);
|
|
5
|
+
var _caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
6
|
+
function raf(callback) {
|
|
7
|
+
return _raf(callback);
|
|
8
|
+
}
|
|
9
|
+
function caf(id) {
|
|
10
|
+
_caf(id);
|
|
11
|
+
}
|
|
12
|
+
function nextFrame() {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
raf(() => resolve());
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function rafThrottle(fn) {
|
|
18
|
+
let pending = false;
|
|
19
|
+
let latestArgs = null;
|
|
20
|
+
const throttled = (...args) => {
|
|
21
|
+
latestArgs = args;
|
|
22
|
+
if (!pending) {
|
|
23
|
+
pending = true;
|
|
24
|
+
raf(() => {
|
|
25
|
+
pending = false;
|
|
26
|
+
if (latestArgs) {
|
|
27
|
+
fn(...latestArgs);
|
|
28
|
+
latestArgs = null;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return throttled;
|
|
34
|
+
}
|
|
35
|
+
function rafDebounce(fn, delay) {
|
|
36
|
+
let id = null;
|
|
37
|
+
let latestArgs = null;
|
|
38
|
+
let remaining = delay ?? 1;
|
|
39
|
+
const debounced = (...args) => {
|
|
40
|
+
latestArgs = args;
|
|
41
|
+
remaining = delay ?? 1;
|
|
42
|
+
if (id !== null) {
|
|
43
|
+
caf(id);
|
|
44
|
+
}
|
|
45
|
+
const tick = () => {
|
|
46
|
+
remaining--;
|
|
47
|
+
if (remaining <= 0) {
|
|
48
|
+
id = null;
|
|
49
|
+
if (latestArgs) {
|
|
50
|
+
fn(...latestArgs);
|
|
51
|
+
latestArgs = null;
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
id = raf(tick);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
id = raf(tick);
|
|
58
|
+
};
|
|
59
|
+
return debounced;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
exports.caf = caf;
|
|
63
|
+
exports.nextFrame = nextFrame;
|
|
64
|
+
exports.raf = raf;
|
|
65
|
+
exports.rafDebounce = rafDebounce;
|
|
66
|
+
exports.rafThrottle = rafThrottle;
|
|
67
|
+
//# sourceMappingURL=index.cjs.map
|
|
68
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAKA,IAAM,IAAA,GACJ,OAAO,qBAAA,KAA0B,WAAA,GAC7B,wBACA,CAAC,EAAA,KACC,UAAA,CAAW,MAAM,EAAA,CAAG,WAAA,CAAY,GAAA,EAAK,GAAG,EAAE,CAAA;AAElD,IAAM,IAAA,GACJ,OAAO,oBAAA,KAAyB,WAAA,GAC5B,uBACA,CAAC,EAAA,KAAqB,aAAa,EAAE,CAAA;AAKpC,SAAS,IAAI,QAAA,EAAwC;AAC1D,EAAA,OAAO,KAAK,QAAQ,CAAA;AACtB;AAKO,SAAS,IAAI,EAAA,EAAkB;AACpC,EAAA,IAAA,CAAK,EAAE,CAAA;AACT;AAKO,SAAS,SAAA,GAA2B;AACzC,EAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACpC,IAAA,GAAA,CAAI,MAAM,SAAS,CAAA;AAAA,EACrB,CAAC,CAAA;AACH;AAKO,SAAS,YAAuD,EAAA,EAAU;AAC/E,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,UAAA,GAA+B,IAAA;AAEnC,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,KAAoB;AACxC,IAAA,UAAA,GAAa,IAAA;AACb,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,GAAA,CAAI,MAAM;AACR,QAAA,OAAA,GAAU,KAAA;AACV,QAAA,IAAI,UAAA,EAAY;AACd,UAAA,EAAA,CAAG,GAAG,UAAU,CAAA;AAChB,UAAA,UAAA,GAAa,IAAA;AAAA,QACf;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,SAAA;AACT;AAKO,SAAS,WAAA,CAAuD,IAAO,KAAA,EAAmB;AAC/F,EAAA,IAAI,EAAA,GAAoB,IAAA;AACxB,EAAA,IAAI,UAAA,GAA+B,IAAA;AACnC,EAAA,IAAI,YAAY,KAAA,IAAS,CAAA;AAEzB,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,KAAoB;AACxC,IAAA,UAAA,GAAa,IAAA;AACb,IAAA,SAAA,GAAY,KAAA,IAAS,CAAA;AAErB,IAAA,IAAI,OAAO,IAAA,EAAM;AACf,MAAA,GAAA,CAAI,EAAE,CAAA;AAAA,IACR;AAEA,IAAA,MAAM,OAAO,MAAM;AACjB,MAAA,SAAA,EAAA;AACA,MAAA,IAAI,aAAa,CAAA,EAAG;AAClB,QAAA,EAAA,GAAK,IAAA;AACL,QAAA,IAAI,UAAA,EAAY;AACd,UAAA,EAAA,CAAG,GAAG,UAAU,CAAA;AAChB,UAAA,UAAA,GAAa,IAAA;AAAA,QACf;AAAA,MACF,CAAA,MAAO;AACL,QAAA,EAAA,GAAK,IAAI,IAAI,CAAA;AAAA,MACf;AAAA,IACF,CAAA;AAEA,IAAA,EAAA,GAAK,IAAI,IAAI,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,OAAO,SAAA;AACT","file":"index.cjs","sourcesContent":["/**\r\n * @lytjs/common-raf\r\n * Cross-platform requestAnimationFrame utilities\r\n */\r\n\r\nconst _raf =\r\n typeof requestAnimationFrame !== 'undefined'\r\n ? requestAnimationFrame\r\n : (cb: FrameRequestCallback): ReturnType<typeof setTimeout> =>\r\n setTimeout(() => cb(performance.now()), 16);\r\n\r\nconst _caf =\r\n typeof cancelAnimationFrame !== 'undefined'\r\n ? cancelAnimationFrame\r\n : (id: number): void => clearTimeout(id);\r\n\r\n/**\r\n * requestAnimationFrame cross-platform wrapper\r\n */\r\nexport function raf(callback: FrameRequestCallback): number {\r\n return _raf(callback) as number;\r\n}\r\n\r\n/**\r\n * cancelAnimationFrame cross-platform wrapper\r\n */\r\nexport function caf(id: number): void {\r\n _caf(id);\r\n}\r\n\r\n/**\r\n * Wait for the next animation frame\r\n */\r\nexport function nextFrame(): Promise<void> {\r\n return new Promise<void>((resolve) => {\r\n raf(() => resolve());\r\n });\r\n}\r\n\r\n/**\r\n * Throttle a function to run at most once per animation frame\r\n */\r\nexport function rafThrottle<T extends (...args: unknown[]) => unknown>(fn: T): T {\r\n let pending = false;\r\n let latestArgs: unknown[] | null = null;\r\n\r\n const throttled = (...args: unknown[]) => {\r\n latestArgs = args;\r\n if (!pending) {\r\n pending = true;\r\n raf(() => {\r\n pending = false;\r\n if (latestArgs) {\r\n fn(...latestArgs);\r\n latestArgs = null;\r\n }\r\n });\r\n }\r\n };\r\n\r\n return throttled as T;\r\n}\r\n\r\n/**\r\n * Debounce a function to run after a specified number of frames (default: 1)\r\n */\r\nexport function rafDebounce<T extends (...args: unknown[]) => unknown>(fn: T, delay?: number): T {\r\n let id: number | null = null;\r\n let latestArgs: unknown[] | null = null;\r\n let remaining = delay ?? 1;\r\n\r\n const debounced = (...args: unknown[]) => {\r\n latestArgs = args;\r\n remaining = delay ?? 1;\r\n\r\n if (id !== null) {\r\n caf(id);\r\n }\r\n\r\n const tick = () => {\r\n remaining--;\r\n if (remaining <= 0) {\r\n id = null;\r\n if (latestArgs) {\r\n fn(...latestArgs);\r\n latestArgs = null;\r\n }\r\n } else {\r\n id = raf(tick);\r\n }\r\n };\r\n\r\n id = raf(tick);\r\n };\r\n\r\n return debounced as T;\r\n}\r\n"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-raf
|
|
3
|
+
* Cross-platform requestAnimationFrame utilities
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* requestAnimationFrame cross-platform wrapper
|
|
7
|
+
*/
|
|
8
|
+
declare function raf(callback: FrameRequestCallback): number;
|
|
9
|
+
/**
|
|
10
|
+
* cancelAnimationFrame cross-platform wrapper
|
|
11
|
+
*/
|
|
12
|
+
declare function caf(id: number): void;
|
|
13
|
+
/**
|
|
14
|
+
* Wait for the next animation frame
|
|
15
|
+
*/
|
|
16
|
+
declare function nextFrame(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Throttle a function to run at most once per animation frame
|
|
19
|
+
*/
|
|
20
|
+
declare function rafThrottle<T extends (...args: unknown[]) => unknown>(fn: T): T;
|
|
21
|
+
/**
|
|
22
|
+
* Debounce a function to run after a specified number of frames (default: 1)
|
|
23
|
+
*/
|
|
24
|
+
declare function rafDebounce<T extends (...args: unknown[]) => unknown>(fn: T, delay?: number): T;
|
|
25
|
+
|
|
26
|
+
export { caf, nextFrame, raf, rafDebounce, rafThrottle };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-raf
|
|
3
|
+
* Cross-platform requestAnimationFrame utilities
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* requestAnimationFrame cross-platform wrapper
|
|
7
|
+
*/
|
|
8
|
+
declare function raf(callback: FrameRequestCallback): number;
|
|
9
|
+
/**
|
|
10
|
+
* cancelAnimationFrame cross-platform wrapper
|
|
11
|
+
*/
|
|
12
|
+
declare function caf(id: number): void;
|
|
13
|
+
/**
|
|
14
|
+
* Wait for the next animation frame
|
|
15
|
+
*/
|
|
16
|
+
declare function nextFrame(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Throttle a function to run at most once per animation frame
|
|
19
|
+
*/
|
|
20
|
+
declare function rafThrottle<T extends (...args: unknown[]) => unknown>(fn: T): T;
|
|
21
|
+
/**
|
|
22
|
+
* Debounce a function to run after a specified number of frames (default: 1)
|
|
23
|
+
*/
|
|
24
|
+
declare function rafDebounce<T extends (...args: unknown[]) => unknown>(fn: T, delay?: number): T;
|
|
25
|
+
|
|
26
|
+
export { caf, nextFrame, raf, rafDebounce, rafThrottle };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var _raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(() => cb(performance.now()), 16);
|
|
3
|
+
var _caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
4
|
+
function raf(callback) {
|
|
5
|
+
return _raf(callback);
|
|
6
|
+
}
|
|
7
|
+
function caf(id) {
|
|
8
|
+
_caf(id);
|
|
9
|
+
}
|
|
10
|
+
function nextFrame() {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
raf(() => resolve());
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function rafThrottle(fn) {
|
|
16
|
+
let pending = false;
|
|
17
|
+
let latestArgs = null;
|
|
18
|
+
const throttled = (...args) => {
|
|
19
|
+
latestArgs = args;
|
|
20
|
+
if (!pending) {
|
|
21
|
+
pending = true;
|
|
22
|
+
raf(() => {
|
|
23
|
+
pending = false;
|
|
24
|
+
if (latestArgs) {
|
|
25
|
+
fn(...latestArgs);
|
|
26
|
+
latestArgs = null;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return throttled;
|
|
32
|
+
}
|
|
33
|
+
function rafDebounce(fn, delay) {
|
|
34
|
+
let id = null;
|
|
35
|
+
let latestArgs = null;
|
|
36
|
+
let remaining = delay ?? 1;
|
|
37
|
+
const debounced = (...args) => {
|
|
38
|
+
latestArgs = args;
|
|
39
|
+
remaining = delay ?? 1;
|
|
40
|
+
if (id !== null) {
|
|
41
|
+
caf(id);
|
|
42
|
+
}
|
|
43
|
+
const tick = () => {
|
|
44
|
+
remaining--;
|
|
45
|
+
if (remaining <= 0) {
|
|
46
|
+
id = null;
|
|
47
|
+
if (latestArgs) {
|
|
48
|
+
fn(...latestArgs);
|
|
49
|
+
latestArgs = null;
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
id = raf(tick);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
id = raf(tick);
|
|
56
|
+
};
|
|
57
|
+
return debounced;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { caf, nextFrame, raf, rafDebounce, rafThrottle };
|
|
61
|
+
//# sourceMappingURL=index.mjs.map
|
|
62
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AAKA,IAAM,IAAA,GACJ,OAAO,qBAAA,KAA0B,WAAA,GAC7B,wBACA,CAAC,EAAA,KACC,UAAA,CAAW,MAAM,EAAA,CAAG,WAAA,CAAY,GAAA,EAAK,GAAG,EAAE,CAAA;AAElD,IAAM,IAAA,GACJ,OAAO,oBAAA,KAAyB,WAAA,GAC5B,uBACA,CAAC,EAAA,KAAqB,aAAa,EAAE,CAAA;AAKpC,SAAS,IAAI,QAAA,EAAwC;AAC1D,EAAA,OAAO,KAAK,QAAQ,CAAA;AACtB;AAKO,SAAS,IAAI,EAAA,EAAkB;AACpC,EAAA,IAAA,CAAK,EAAE,CAAA;AACT;AAKO,SAAS,SAAA,GAA2B;AACzC,EAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACpC,IAAA,GAAA,CAAI,MAAM,SAAS,CAAA;AAAA,EACrB,CAAC,CAAA;AACH;AAKO,SAAS,YAAuD,EAAA,EAAU;AAC/E,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,UAAA,GAA+B,IAAA;AAEnC,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,KAAoB;AACxC,IAAA,UAAA,GAAa,IAAA;AACb,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,GAAA,CAAI,MAAM;AACR,QAAA,OAAA,GAAU,KAAA;AACV,QAAA,IAAI,UAAA,EAAY;AACd,UAAA,EAAA,CAAG,GAAG,UAAU,CAAA;AAChB,UAAA,UAAA,GAAa,IAAA;AAAA,QACf;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,SAAA;AACT;AAKO,SAAS,WAAA,CAAuD,IAAO,KAAA,EAAmB;AAC/F,EAAA,IAAI,EAAA,GAAoB,IAAA;AACxB,EAAA,IAAI,UAAA,GAA+B,IAAA;AACnC,EAAA,IAAI,YAAY,KAAA,IAAS,CAAA;AAEzB,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,KAAoB;AACxC,IAAA,UAAA,GAAa,IAAA;AACb,IAAA,SAAA,GAAY,KAAA,IAAS,CAAA;AAErB,IAAA,IAAI,OAAO,IAAA,EAAM;AACf,MAAA,GAAA,CAAI,EAAE,CAAA;AAAA,IACR;AAEA,IAAA,MAAM,OAAO,MAAM;AACjB,MAAA,SAAA,EAAA;AACA,MAAA,IAAI,aAAa,CAAA,EAAG;AAClB,QAAA,EAAA,GAAK,IAAA;AACL,QAAA,IAAI,UAAA,EAAY;AACd,UAAA,EAAA,CAAG,GAAG,UAAU,CAAA;AAChB,UAAA,UAAA,GAAa,IAAA;AAAA,QACf;AAAA,MACF,CAAA,MAAO;AACL,QAAA,EAAA,GAAK,IAAI,IAAI,CAAA;AAAA,MACf;AAAA,IACF,CAAA;AAEA,IAAA,EAAA,GAAK,IAAI,IAAI,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,OAAO,SAAA;AACT","file":"index.mjs","sourcesContent":["/**\r\n * @lytjs/common-raf\r\n * Cross-platform requestAnimationFrame utilities\r\n */\r\n\r\nconst _raf =\r\n typeof requestAnimationFrame !== 'undefined'\r\n ? requestAnimationFrame\r\n : (cb: FrameRequestCallback): ReturnType<typeof setTimeout> =>\r\n setTimeout(() => cb(performance.now()), 16);\r\n\r\nconst _caf =\r\n typeof cancelAnimationFrame !== 'undefined'\r\n ? cancelAnimationFrame\r\n : (id: number): void => clearTimeout(id);\r\n\r\n/**\r\n * requestAnimationFrame cross-platform wrapper\r\n */\r\nexport function raf(callback: FrameRequestCallback): number {\r\n return _raf(callback) as number;\r\n}\r\n\r\n/**\r\n * cancelAnimationFrame cross-platform wrapper\r\n */\r\nexport function caf(id: number): void {\r\n _caf(id);\r\n}\r\n\r\n/**\r\n * Wait for the next animation frame\r\n */\r\nexport function nextFrame(): Promise<void> {\r\n return new Promise<void>((resolve) => {\r\n raf(() => resolve());\r\n });\r\n}\r\n\r\n/**\r\n * Throttle a function to run at most once per animation frame\r\n */\r\nexport function rafThrottle<T extends (...args: unknown[]) => unknown>(fn: T): T {\r\n let pending = false;\r\n let latestArgs: unknown[] | null = null;\r\n\r\n const throttled = (...args: unknown[]) => {\r\n latestArgs = args;\r\n if (!pending) {\r\n pending = true;\r\n raf(() => {\r\n pending = false;\r\n if (latestArgs) {\r\n fn(...latestArgs);\r\n latestArgs = null;\r\n }\r\n });\r\n }\r\n };\r\n\r\n return throttled as T;\r\n}\r\n\r\n/**\r\n * Debounce a function to run after a specified number of frames (default: 1)\r\n */\r\nexport function rafDebounce<T extends (...args: unknown[]) => unknown>(fn: T, delay?: number): T {\r\n let id: number | null = null;\r\n let latestArgs: unknown[] | null = null;\r\n let remaining = delay ?? 1;\r\n\r\n const debounced = (...args: unknown[]) => {\r\n latestArgs = args;\r\n remaining = delay ?? 1;\r\n\r\n if (id !== null) {\r\n caf(id);\r\n }\r\n\r\n const tick = () => {\r\n remaining--;\r\n if (remaining <= 0) {\r\n id = null;\r\n if (latestArgs) {\r\n fn(...latestArgs);\r\n latestArgs = null;\r\n }\r\n } else {\r\n id = raf(tick);\r\n }\r\n };\r\n\r\n id = raf(tick);\r\n };\r\n\r\n return debounced as T;\r\n}\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lytjs/common-raf",
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "Cross-platform requestAnimationFrame utilities for LytJS",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"test:coverage": "vitest run --coverage",
|
|
23
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"type-check": "tsc --noEmit",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsup": "^8.4.0",
|
|
32
|
+
"typescript": "^5.8.2",
|
|
33
|
+
"vitest": "^3.0.7"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-raf
|
|
3
|
+
* Cross-platform requestAnimationFrame utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const _raf =
|
|
7
|
+
typeof requestAnimationFrame !== 'undefined'
|
|
8
|
+
? requestAnimationFrame
|
|
9
|
+
: (cb: FrameRequestCallback): ReturnType<typeof setTimeout> =>
|
|
10
|
+
setTimeout(() => cb(performance.now()), 16);
|
|
11
|
+
|
|
12
|
+
const _caf =
|
|
13
|
+
typeof cancelAnimationFrame !== 'undefined'
|
|
14
|
+
? cancelAnimationFrame
|
|
15
|
+
: (id: number): void => clearTimeout(id);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* requestAnimationFrame cross-platform wrapper
|
|
19
|
+
*/
|
|
20
|
+
export function raf(callback: FrameRequestCallback): number {
|
|
21
|
+
return _raf(callback) as number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* cancelAnimationFrame cross-platform wrapper
|
|
26
|
+
*/
|
|
27
|
+
export function caf(id: number): void {
|
|
28
|
+
_caf(id);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Wait for the next animation frame
|
|
33
|
+
*/
|
|
34
|
+
export function nextFrame(): Promise<void> {
|
|
35
|
+
return new Promise<void>((resolve) => {
|
|
36
|
+
raf(() => resolve());
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Throttle a function to run at most once per animation frame
|
|
42
|
+
*/
|
|
43
|
+
export function rafThrottle<T extends (...args: unknown[]) => unknown>(fn: T): T {
|
|
44
|
+
let pending = false;
|
|
45
|
+
let latestArgs: unknown[] | null = null;
|
|
46
|
+
|
|
47
|
+
const throttled = (...args: unknown[]) => {
|
|
48
|
+
latestArgs = args;
|
|
49
|
+
if (!pending) {
|
|
50
|
+
pending = true;
|
|
51
|
+
raf(() => {
|
|
52
|
+
pending = false;
|
|
53
|
+
if (latestArgs) {
|
|
54
|
+
fn(...latestArgs);
|
|
55
|
+
latestArgs = null;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return throttled as T;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Debounce a function to run after a specified number of frames (default: 1)
|
|
66
|
+
*/
|
|
67
|
+
export function rafDebounce<T extends (...args: unknown[]) => unknown>(fn: T, delay?: number): T {
|
|
68
|
+
let id: number | null = null;
|
|
69
|
+
let latestArgs: unknown[] | null = null;
|
|
70
|
+
let remaining = delay ?? 1;
|
|
71
|
+
|
|
72
|
+
const debounced = (...args: unknown[]) => {
|
|
73
|
+
latestArgs = args;
|
|
74
|
+
remaining = delay ?? 1;
|
|
75
|
+
|
|
76
|
+
if (id !== null) {
|
|
77
|
+
caf(id);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const tick = () => {
|
|
81
|
+
remaining--;
|
|
82
|
+
if (remaining <= 0) {
|
|
83
|
+
id = null;
|
|
84
|
+
if (latestArgs) {
|
|
85
|
+
fn(...latestArgs);
|
|
86
|
+
latestArgs = null;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
id = raf(tick);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
id = raf(tick);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return debounced as T;
|
|
97
|
+
}
|