upwwwards 0.1.7-beta.1 → 0.1.8-beta.1
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/{Thread-Cvf__-O4.d.ts → Thread-5NGNyOJb.d.ts} +98 -49
- package/dist/core/[1]/[2]/Thread.js +137 -95
- package/dist/core/[1]/[2]/Thread.js.map +1 -1
- package/dist/core/[1]/[2]/[3]/App.js +12 -12
- package/dist/core/[1]/[2]/[3]/App.js.map +1 -1
- package/dist/{index-CzWXovKy.d.ts → index-DWhRKejo.d.ts} +16 -12
- package/dist/index.d.ts +3 -3
- package/dist/initializers/vite.d.ts +2 -2
- package/dist/plugins/@/Lazy.js +2 -2
- package/dist/plugins/@/Lazy.js.map +1 -1
- package/dist/plugins/Anime.d.ts +2 -2
- package/dist/plugins/Anime.js +8 -1
- package/dist/plugins/Anime.js.map +1 -1
- package/dist/plugins/Puppeteer.d.ts +2 -2
- package/dist/plugins/Scroll.d.ts +2 -2
- package/dist/plugins/Scroll.js +19 -18
- package/dist/plugins/Scroll.js.map +1 -1
- package/dist/plugins/Synthra.d.ts +2 -2
- package/dist/plugins/Transmitter.d.ts +2 -2
- package/dist/plugins/index.d.ts +2 -2
- package/dist/scripts/@arrays/Callstack.js +12 -0
- package/dist/scripts/@arrays/Callstack.js.map +1 -1
- package/dist/scripts/Arrays.d.ts +1 -1
- package/dist/scripts/index.d.ts +1 -1
- package/docs/core.md +12 -6
- package/docs/faq.md +1 -1
- package/docs/plugins/anime.md +2 -0
- package/docs/plugins/scroll.md +4 -13
- package/docs/scripts/arrays.md +3 -1
- package/llms.txt +3 -1
- package/package.json +1 -1
|
@@ -8,13 +8,14 @@ declare namespace CallStack {
|
|
|
8
8
|
type CALLBACK = (...args: any) => void;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Ordered list of synchronous callbacks invoked together, in insertion order, with the same arguments
|
|
12
|
+
* (a lightweight single-event emitter).
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* without skipping the next callback,
|
|
14
|
+
* Run them all at once with {@link CallStack.loop}, or one-by-one with {@link CallStack.gen} (yields after each
|
|
15
|
+
* callback so a caller such as `Up.Thread` can spread work across frames).
|
|
16
|
+
* You may {@link CallStack.add}, {@link CallStack.remove}, or {@link CallStack.clear} while an iteration is running:
|
|
17
|
+
* removals apply immediately without skipping the next callback, additions run from the next iteration,
|
|
18
|
+
* and `clear` skips any callback that has not run yet.
|
|
18
19
|
*
|
|
19
20
|
* If a callback throws, the error is logged with `Logs.error` and the remaining callbacks of that
|
|
20
21
|
* iteration are skipped; the error is not rethrown.
|
|
@@ -93,6 +94,12 @@ declare class CallStack<T extends CallStack.CALLBACK> {
|
|
|
93
94
|
* ```
|
|
94
95
|
*/
|
|
95
96
|
gen(...args: Parameters<T>): Thread.GENERATOR;
|
|
97
|
+
/**
|
|
98
|
+
* Removes every callback and resets iteration counters (`length` becomes `0`).
|
|
99
|
+
* Safe to call during {@link CallStack.loop} / {@link CallStack.gen}: callbacks that have not run yet are skipped.
|
|
100
|
+
* When no iteration is in progress, any outstanding generator created by {@link CallStack.gen} is closed.
|
|
101
|
+
*/
|
|
102
|
+
clear(): void;
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
declare namespace Arrays {
|
|
@@ -294,16 +301,6 @@ declare const
|
|
|
294
301
|
* Key of the method returning the active cache (browser map or request-scoped server map).
|
|
295
302
|
*/
|
|
296
303
|
getCache: unique symbol;
|
|
297
|
-
declare const /**
|
|
298
|
-
* Key of the modifier refreshing viewport metrics.
|
|
299
|
-
* @internal
|
|
300
|
-
*/
|
|
301
|
-
updateScreenVars: unique symbol;
|
|
302
|
-
declare const /**
|
|
303
|
-
* Key of the modifier refreshing scroll metrics.
|
|
304
|
-
* @internal
|
|
305
|
-
*/
|
|
306
|
-
updateScrollVars: unique symbol;
|
|
307
304
|
declare namespace App {
|
|
308
305
|
/**
|
|
309
306
|
* Operating system / device family detected from `navigator.userAgent`.
|
|
@@ -332,8 +329,9 @@ declare namespace App {
|
|
|
332
329
|
/**
|
|
333
330
|
* Application runtime: device and browser detection, viewport and scroll metrics, an application cache,
|
|
334
331
|
* and a wait queue that delays the `LOAD` event (see {@link Event}).
|
|
335
|
-
* @note Metrics are client-only. They are read once by `Up.set`, then refreshed by Thread modifiers
|
|
336
|
-
* `resize` (flag `8`) and `scroll` (flag `32`)
|
|
332
|
+
* @note Metrics are client-only. They are read once by `Up.set`, then refreshed by Thread modifiers on
|
|
333
|
+
* `resize` (flag `8`) and `scroll` (flag `32`) when those flags are armed ({@link Thread.sub} / {@link Thread.arm}).
|
|
334
|
+
* With the Scroll plugin, Lenis arms the scroll flag after writing `window.scrollY`.
|
|
337
335
|
*/
|
|
338
336
|
declare abstract class App {
|
|
339
337
|
#private;
|
|
@@ -489,15 +487,16 @@ declare abstract class App {
|
|
|
489
487
|
*/
|
|
490
488
|
static remove(key: App.CACHE_KEY): boolean;
|
|
491
489
|
/**
|
|
492
|
-
*
|
|
493
|
-
* @
|
|
490
|
+
* Refreshes viewport metrics (`w`, `cw`, `sw`, `lw`, `h`, `ch`, `sh`, `lh`).
|
|
491
|
+
* Registered as a Thread modifier on {@link Thread.RESIZE_FLAG}; call it yourself only if you need an immediate read outside that phase.
|
|
494
492
|
*/
|
|
495
|
-
static
|
|
493
|
+
static updateScreenVars(): void;
|
|
496
494
|
/**
|
|
497
|
-
*
|
|
498
|
-
* @
|
|
495
|
+
* Refreshes scroll metrics (`y`, `dY`, `downwards`) from `window.scrollY`.
|
|
496
|
+
* Registered as a Thread modifier on {@link Thread.RESIZE_FLAG} and {@link Thread.SCROLL_FLAG}; no-op when `y` is unchanged.
|
|
497
|
+
* Prefer letting Thread arm those flags (or the Scroll plugin) rather than calling this every frame by hand.
|
|
499
498
|
*/
|
|
500
|
-
static
|
|
499
|
+
static updateScrollVars(): void;
|
|
501
500
|
/**
|
|
502
501
|
* Clears the application cache and pending wait queue. Called from `Up.destroy` in the browser.
|
|
503
502
|
* @internal
|
|
@@ -518,15 +517,15 @@ declare namespace Thread {
|
|
|
518
517
|
* Thread scheduler configuration, declared in `up.thread` of the Vite initializer.
|
|
519
518
|
*/
|
|
520
519
|
type CONFIG = {
|
|
521
|
-
/**
|
|
522
|
-
* Soft time budget in ms per frame for flow (one-shot) tasks. Must be greater than `0`. Default: `8`.
|
|
523
|
-
*/
|
|
524
|
-
flowBudget?: number;
|
|
525
520
|
/**
|
|
526
521
|
* Soft time budget in ms per frame for forever tasks. Must be greater than `0`. Default: `4`.
|
|
527
522
|
* Forever tasks always run every frame; the budget only limits how long their generators may run.
|
|
528
523
|
*/
|
|
529
524
|
foreverBudget?: number;
|
|
525
|
+
/**
|
|
526
|
+
* Soft time budget in ms per frame for flow (one-shot) tasks. Must be greater than `0`. Default: `8`.
|
|
527
|
+
*/
|
|
528
|
+
flowBudget?: number;
|
|
530
529
|
};
|
|
531
530
|
/**
|
|
532
531
|
* Flag of `EARTHQUAKE` event tasks (`2`).
|
|
@@ -553,10 +552,20 @@ declare namespace Thread {
|
|
|
553
552
|
*/
|
|
554
553
|
type SYNTHRA_FLAG = 2_048;
|
|
555
554
|
/**
|
|
556
|
-
*
|
|
557
|
-
*
|
|
555
|
+
* Default flag of {@link Thread.sub} when `flag` is omitted (`8192`).
|
|
556
|
+
* Runs before {@link Thread.MAX_FLAG} (end-of-frame work such as Anime `updateRoot`).
|
|
557
|
+
*/
|
|
558
|
+
type DEFAULT_FLAG = 8_192;
|
|
559
|
+
/**
|
|
560
|
+
* Highest allowed priority flag (`32768`). Reserved for end-of-frame work (e.g. Anime advances GSAP here).
|
|
561
|
+
* Not the default of {@link Thread.sub} — see {@link Thread.DEFAULT_FLAG}.
|
|
558
562
|
*/
|
|
559
|
-
type
|
|
563
|
+
type MAX_FLAG = 32_768;
|
|
564
|
+
/**
|
|
565
|
+
* Priority flag of a task or modifier: a power of 2 from `1` to {@link Thread.MAX_FLAG} (`32768`).
|
|
566
|
+
* Within a frame, lower flags run first; for each flag, forever tasks run before flow tasks.
|
|
567
|
+
*/
|
|
568
|
+
type FLAG = 1 | EARTHQUAKE_FLAG | 4 | RESIZE_FLAG | 16 | SCROLL_FLAG | 64 | EVENT_FLAG | 256 | OBSERVER_FLAG | 1_024 | SYNTHRA_FLAG | 4_096 | DEFAULT_FLAG | 16_384 | MAX_FLAG;
|
|
560
569
|
/**
|
|
561
570
|
* Modifier callback. It is called without arguments and its return value is ignored.
|
|
562
571
|
*/
|
|
@@ -602,10 +611,12 @@ declare namespace Thread {
|
|
|
602
611
|
];
|
|
603
612
|
}
|
|
604
613
|
/**
|
|
605
|
-
* Frame scheduler of the runtime. Each frame (`requestAnimationFrame`, or an external ticker such as GSAP)
|
|
606
|
-
*
|
|
607
|
-
*
|
|
608
|
-
*
|
|
614
|
+
* Frame scheduler of the runtime (`Up.Thread`). Each frame (`requestAnimationFrame`, or an external ticker such as GSAP)
|
|
615
|
+
* runs, in order: **drivers** → **modifiers** → **tasks by ascending flag** (forever then flow for each flag).
|
|
616
|
+
* Drivers (see {@link Thread.addDriver}) write frame inputs early (e.g. Lenis `raf`). Modifiers refresh shared data
|
|
617
|
+
* before tasks; arm them with {@link Thread.sub} or {@link Thread.arm}. Soft budgets ({@link Thread.CONFIG}) apply
|
|
618
|
+
* separately to forever and flow. Prefer `Up.Event.sub(['TICK', …])` for simple per-frame work; use Thread for
|
|
619
|
+
* budgets, priorities, drivers, or end-of-frame forever tasks on {@link Thread.MAX_FLAG}.
|
|
609
620
|
* @note Client-only: the frame loop starts with `Up.set`. The Thread is paused while the page is hidden.
|
|
610
621
|
*/
|
|
611
622
|
declare abstract class Thread {
|
|
@@ -632,10 +643,15 @@ declare abstract class Thread {
|
|
|
632
643
|
*/
|
|
633
644
|
static get App(): typeof App;
|
|
634
645
|
/**
|
|
635
|
-
*
|
|
636
|
-
* @returns `
|
|
646
|
+
* Default priority flag of {@link Thread.sub} when `flag` is omitted (`8192`).
|
|
647
|
+
* @returns `8192`.
|
|
637
648
|
*/
|
|
638
|
-
static get
|
|
649
|
+
static get DEFAULT_FLAG(): Thread.DEFAULT_FLAG;
|
|
650
|
+
/**
|
|
651
|
+
* Highest allowed priority flag (`32768`). Use for end-of-frame forever work (e.g. Anime `gsap.updateRoot`).
|
|
652
|
+
* @returns `32768`.
|
|
653
|
+
*/
|
|
654
|
+
static get MAX_FLAG(): Thread.MAX_FLAG;
|
|
639
655
|
/**
|
|
640
656
|
* Timestamp (ms) of the last processed frame, in the clock of the frame driver:
|
|
641
657
|
* `requestAnimationFrame` time by default, or the external ticker time when one is attached (e.g. GSAP ticker time × `1000`).
|
|
@@ -662,16 +678,32 @@ declare abstract class Thread {
|
|
|
662
678
|
*/
|
|
663
679
|
static [set](): void;
|
|
664
680
|
/**
|
|
665
|
-
* Registers a
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
681
|
+
* Registers a **driver**: a sync callback run at the **start** of every processed frame, before modifiers and tasks.
|
|
682
|
+
* Use it for external clocks that write frame inputs (e.g. Scroll registers Lenis `raf` here with `autoRaf: false`).
|
|
683
|
+
* Callbacks run in registration order with no arguments; prefer sync functions. The same reference can be
|
|
684
|
+
* registered only once (a duplicate logs a warning and is ignored).
|
|
685
|
+
* @param callback Function called at the start of the frame (reads {@link Thread.timestamp} if needed).
|
|
686
|
+
* @example
|
|
687
|
+
* ```ts
|
|
688
|
+
* const step = (): void => { lenis.raf(Up.Thread.timestamp) }
|
|
689
|
+
*
|
|
690
|
+
* Up.Thread.addDriver(step)
|
|
691
|
+
* // later
|
|
692
|
+
* Up.Thread.removeDriver(step)
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
static addDriver(callback: Thread.CALLBACK): void;
|
|
696
|
+
/**
|
|
697
|
+
* Registers a modifier: a lightweight hook that refreshes shared data before tasks of the same `flag`.
|
|
698
|
+
* After {@link Thread.sub} or {@link Thread.arm} with that flag, the modifier runs once in the modifiers phase
|
|
699
|
+
* of the next processed frame (at most once per frame), before forever/flow tasks. Modifiers run by ascending
|
|
700
|
+
* flag, then in registration order. Registering the same callback twice on the same flag logs a warning.
|
|
669
701
|
* @note Keep modifiers cheap: they may run on many frames.
|
|
670
702
|
* @note The `App` metric modifiers (on {@link Thread.RESIZE_FLAG} and {@link Thread.SCROLL_FLAG}) are registered first,
|
|
671
703
|
* so they run before user modifiers of the same flag.
|
|
672
704
|
* @param callback Function called without arguments.
|
|
673
|
-
* @param flag Priority flag, a power of 2 from `1` to
|
|
674
|
-
* @throws If `flag` is not a power of 2 between `1` and
|
|
705
|
+
* @param flag Priority flag, a power of 2 from `1` to {@link Thread.MAX_FLAG}.
|
|
706
|
+
* @throws If `flag` is not a power of 2 between `1` and {@link Thread.MAX_FLAG}.
|
|
675
707
|
* @example
|
|
676
708
|
* ```ts
|
|
677
709
|
* let ratio = 1
|
|
@@ -680,15 +712,20 @@ declare abstract class Thread {
|
|
|
680
712
|
* ```
|
|
681
713
|
*/
|
|
682
714
|
static addModifier(callback: Thread.CALLBACK, flag: Thread.FLAG): void;
|
|
715
|
+
/**
|
|
716
|
+
* Unregisters a driver added with {@link Thread.addDriver}. Does nothing if it is not registered.
|
|
717
|
+
* @param callback The same function reference passed to `addDriver`.
|
|
718
|
+
*/
|
|
719
|
+
static removeDriver(callback: Thread.CALLBACK): void;
|
|
683
720
|
/**
|
|
684
721
|
* Removes a modifier registered with {@link Thread.addModifier}. Does nothing if it is not registered on `flag`.
|
|
685
722
|
* @param callback The same function reference passed to `addModifier`.
|
|
686
723
|
* @param flag The flag used when adding the modifier.
|
|
687
|
-
* @throws If `flag` is not a power of 2 between `1` and
|
|
724
|
+
* @throws If `flag` is not a power of 2 between `1` and {@link Thread.MAX_FLAG}.
|
|
688
725
|
*/
|
|
689
726
|
static removeModifier(callback: Thread.CALLBACK, flag: Thread.FLAG): void;
|
|
690
727
|
/**
|
|
691
|
-
* Clears all modifiers and
|
|
728
|
+
* Clears all modifiers, tasks, and drivers, and stops the internal frame loop. Called from `Up.destroy` in the browser.
|
|
692
729
|
* @internal
|
|
693
730
|
*/
|
|
694
731
|
static [destroy](): void;
|
|
@@ -722,9 +759,9 @@ declare abstract class Thread {
|
|
|
722
759
|
* (the task stays scheduled until the promise settles).
|
|
723
760
|
* @template T Task type, used to type `args`.
|
|
724
761
|
* @param task The task function, identified by reference.
|
|
725
|
-
* @param flag Priority flag, a power of 2 from `1` to
|
|
762
|
+
* @param flag Priority flag, a power of 2 from `1` to {@link Thread.MAX_FLAG}. Default: {@link Thread.DEFAULT_FLAG} (`8192`).
|
|
726
763
|
* @param args Arguments passed to the task when it runs (the latest call wins).
|
|
727
|
-
* @throws If `flag` is not a power of 2 between `1` and `
|
|
764
|
+
* @throws If `flag` is not a power of 2 between `1` and {@link Thread.MAX_FLAG} (`32768`).
|
|
728
765
|
* @example
|
|
729
766
|
* ```ts
|
|
730
767
|
* function* build(items: HTMLElement[]): Generator<void, void, void>
|
|
@@ -747,6 +784,18 @@ declare abstract class Thread {
|
|
|
747
784
|
* @param task The same function reference passed to {@link Thread.sub}.
|
|
748
785
|
*/
|
|
749
786
|
static unsub(task: Thread.TASK): void;
|
|
787
|
+
/**
|
|
788
|
+
* Arms modifiers of `flag` for the next modifiers phase (same frame if called from a driver or before modifiers run).
|
|
789
|
+
* No-op when no modifiers are registered on `flag`. {@link Thread.sub} calls this automatically; call it yourself
|
|
790
|
+
* when an external writer updates shared state without scheduling a task (e.g. Scroll arms {@link Thread.SCROLL_FLAG}
|
|
791
|
+
* after Lenis writes `window.scrollY`, so `App.updateScrollVars` and ScrollTrigger modifiers run).
|
|
792
|
+
* @param flag Priority flag whose modifiers should run.
|
|
793
|
+
* @example
|
|
794
|
+
* ```ts
|
|
795
|
+
* lenis.on('scroll', () => Up.Thread.arm(32))
|
|
796
|
+
* ```
|
|
797
|
+
*/
|
|
798
|
+
static arm(flag: Thread.FLAG): void;
|
|
750
799
|
/**
|
|
751
800
|
* Returns a promise that resolves when a scheduled task completes its next (or current) run, or when it is unsubscribed.
|
|
752
801
|
* @param task The same function reference passed to {@link Thread.sub}.
|
|
@@ -781,4 +830,4 @@ declare abstract class Thread {
|
|
|
781
830
|
static unmarkForever(task: Thread.TASK): void;
|
|
782
831
|
}
|
|
783
832
|
|
|
784
|
-
export { Arrays as A, CallStack as C, Thread as T, App as a, type AppCache as b
|
|
833
|
+
export { Arrays as A, CallStack as C, Thread as T, App as a, type AppCache as b };
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { INHERITANCE, STATUS, construct, set, destroy, apply, start, stop } from "../../../symbols/index.js";
|
|
2
|
+
import Arrays from "../../../scripts/Arrays.js";
|
|
2
3
|
import Logs from "../../../scripts/Logs.js";
|
|
3
|
-
import App
|
|
4
|
+
import App from "./[3]/App.js";
|
|
4
5
|
const FOREVER = /* @__PURE__ */ Symbol("FOREVER");
|
|
5
6
|
class Thread {
|
|
6
7
|
// #PROPS
|
|
7
8
|
// ##STATICS
|
|
8
|
-
static #
|
|
9
|
+
static #DEFAULT_FLAG = 8192;
|
|
10
|
+
static #MAX_FLAG = 32768;
|
|
9
11
|
static #MODIFIERS_MAP = /* @__PURE__ */ new Map();
|
|
10
|
-
static #FLOW_MAP = /* @__PURE__ */ new Map();
|
|
11
12
|
static #FOREVER_MAP = /* @__PURE__ */ new Map();
|
|
12
|
-
static #
|
|
13
|
+
static #FLOW_MAP = /* @__PURE__ */ new Map();
|
|
13
14
|
static #FOREVER_LISTED = /* @__PURE__ */ new Map();
|
|
15
|
+
static #FLOW_LISTED = /* @__PURE__ */ new Map();
|
|
16
|
+
static #DRIVERS_CALLSTACK = new Arrays.CallStack();
|
|
14
17
|
/**
|
|
15
18
|
* Shared HRT stack after hydrating `App` (`[Adapter, App]`); later modules append `Thread`, `Event`.
|
|
16
19
|
* Same array as `App[INHERITANCE]` — does not include `Thread` itself.
|
|
@@ -26,8 +29,8 @@ class Thread {
|
|
|
26
29
|
static #flowBudget = 8;
|
|
27
30
|
static #foreverBudget = 4;
|
|
28
31
|
static #modifiersBitMask = 0;
|
|
29
|
-
static #flowBitMask = 0;
|
|
30
32
|
static #foreverBitMask = 0;
|
|
33
|
+
static #flowBitMask = 0;
|
|
31
34
|
static #i = -1;
|
|
32
35
|
static #max = -1;
|
|
33
36
|
static #stack = null;
|
|
@@ -54,8 +57,15 @@ class Thread {
|
|
|
54
57
|
return App;
|
|
55
58
|
}
|
|
56
59
|
/**
|
|
57
|
-
*
|
|
58
|
-
* @returns `
|
|
60
|
+
* Default priority flag of {@link Thread.sub} when `flag` is omitted (`8192`).
|
|
61
|
+
* @returns `8192`.
|
|
62
|
+
*/
|
|
63
|
+
static get DEFAULT_FLAG() {
|
|
64
|
+
return Thread.#DEFAULT_FLAG;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Highest allowed priority flag (`32768`). Use for end-of-frame forever work (e.g. Anime `gsap.updateRoot`).
|
|
68
|
+
* @returns `32768`.
|
|
59
69
|
*/
|
|
60
70
|
static get MAX_FLAG() {
|
|
61
71
|
return Thread.#MAX_FLAG;
|
|
@@ -91,9 +101,9 @@ class Thread {
|
|
|
91
101
|
if (config.flowBudget) config.flowBudget > 0 ? Thread.#flowBudget = config.flowBudget : Logs.warn("THREAD", "THE FLOW BUDGET MUST BE GREATER THAN 0.");
|
|
92
102
|
if (config.foreverBudget) config.foreverBudget > 0 ? Thread.#foreverBudget = config.foreverBudget : Logs.warn("THREAD", "THE FOREVER BUDGET MUST BE GREATER THAN 0.");
|
|
93
103
|
}
|
|
94
|
-
Thread.addModifier(Thread.App
|
|
95
|
-
Thread.addModifier(Thread.App
|
|
96
|
-
Thread.addModifier(Thread.App
|
|
104
|
+
Thread.addModifier(Thread.App.updateScreenVars, 8);
|
|
105
|
+
Thread.addModifier(Thread.App.updateScrollVars, 8);
|
|
106
|
+
Thread.addModifier(Thread.App.updateScrollVars, 32);
|
|
97
107
|
}
|
|
98
108
|
// ##SET
|
|
99
109
|
/**
|
|
@@ -113,10 +123,10 @@ class Thread {
|
|
|
113
123
|
}
|
|
114
124
|
// ##GET
|
|
115
125
|
/**
|
|
116
|
-
* Returns `flag` after validation, or `
|
|
126
|
+
* Returns `flag` after validation, or `DEFAULT_FLAG` when omitted.
|
|
117
127
|
*/
|
|
118
128
|
static #getFlagFrom(flag) {
|
|
119
|
-
if (flag == null) return Thread.#
|
|
129
|
+
if (flag == null) return Thread.#DEFAULT_FLAG;
|
|
120
130
|
Thread.#checkFlag(flag);
|
|
121
131
|
return flag;
|
|
122
132
|
}
|
|
@@ -144,16 +154,35 @@ class Thread {
|
|
|
144
154
|
map.get(flag)?.push(wrapper) ?? map.set(flag, [wrapper]);
|
|
145
155
|
}
|
|
146
156
|
/**
|
|
147
|
-
* Registers a
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
157
|
+
* Registers a **driver**: a sync callback run at the **start** of every processed frame, before modifiers and tasks.
|
|
158
|
+
* Use it for external clocks that write frame inputs (e.g. Scroll registers Lenis `raf` here with `autoRaf: false`).
|
|
159
|
+
* Callbacks run in registration order with no arguments; prefer sync functions. The same reference can be
|
|
160
|
+
* registered only once (a duplicate logs a warning and is ignored).
|
|
161
|
+
* @param callback Function called at the start of the frame (reads {@link Thread.timestamp} if needed).
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* const step = (): void => { lenis.raf(Up.Thread.timestamp) }
|
|
165
|
+
*
|
|
166
|
+
* Up.Thread.addDriver(step)
|
|
167
|
+
* // later
|
|
168
|
+
* Up.Thread.removeDriver(step)
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
static addDriver(callback) {
|
|
172
|
+
if (Thread.#DRIVERS_CALLSTACK.has(callback)) return Logs.warn("THREAD", "THE DRIVER CALLBACK IS ALREADY REGISTERED.");
|
|
173
|
+
Thread.#DRIVERS_CALLSTACK.add(callback);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Registers a modifier: a lightweight hook that refreshes shared data before tasks of the same `flag`.
|
|
177
|
+
* After {@link Thread.sub} or {@link Thread.arm} with that flag, the modifier runs once in the modifiers phase
|
|
178
|
+
* of the next processed frame (at most once per frame), before forever/flow tasks. Modifiers run by ascending
|
|
179
|
+
* flag, then in registration order. Registering the same callback twice on the same flag logs a warning.
|
|
151
180
|
* @note Keep modifiers cheap: they may run on many frames.
|
|
152
181
|
* @note The `App` metric modifiers (on {@link Thread.RESIZE_FLAG} and {@link Thread.SCROLL_FLAG}) are registered first,
|
|
153
182
|
* so they run before user modifiers of the same flag.
|
|
154
183
|
* @param callback Function called without arguments.
|
|
155
|
-
* @param flag Priority flag, a power of 2 from `1` to
|
|
156
|
-
* @throws If `flag` is not a power of 2 between `1` and
|
|
184
|
+
* @param flag Priority flag, a power of 2 from `1` to {@link Thread.MAX_FLAG}.
|
|
185
|
+
* @throws If `flag` is not a power of 2 between `1` and {@link Thread.MAX_FLAG}.
|
|
157
186
|
* @example
|
|
158
187
|
* ```ts
|
|
159
188
|
* let ratio = 1
|
|
@@ -202,11 +231,18 @@ class Thread {
|
|
|
202
231
|
STACK.splice(INDEX, 1);
|
|
203
232
|
Thread.#updateCurrentAfterRemoving(STACK, INDEX);
|
|
204
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Unregisters a driver added with {@link Thread.addDriver}. Does nothing if it is not registered.
|
|
236
|
+
* @param callback The same function reference passed to `addDriver`.
|
|
237
|
+
*/
|
|
238
|
+
static removeDriver(callback) {
|
|
239
|
+
Thread.#DRIVERS_CALLSTACK.remove(callback);
|
|
240
|
+
}
|
|
205
241
|
/**
|
|
206
242
|
* Removes a modifier registered with {@link Thread.addModifier}. Does nothing if it is not registered on `flag`.
|
|
207
243
|
* @param callback The same function reference passed to `addModifier`.
|
|
208
244
|
* @param flag The flag used when adding the modifier.
|
|
209
|
-
* @throws If `flag` is not a power of 2 between `1` and
|
|
245
|
+
* @throws If `flag` is not a power of 2 between `1` and {@link Thread.MAX_FLAG}.
|
|
210
246
|
*/
|
|
211
247
|
static removeModifier(callback, flag) {
|
|
212
248
|
Thread.#checkFlag(flag);
|
|
@@ -220,7 +256,7 @@ class Thread {
|
|
|
220
256
|
}
|
|
221
257
|
// ##UPDATES
|
|
222
258
|
/**
|
|
223
|
-
* One scheduler frame: refresh `#delta` / `#timestamp`, then
|
|
259
|
+
* One scheduler frame: refresh `#delta` / `#timestamp`, then drivers → modifiers → tasks (forever then flow per flag).
|
|
224
260
|
* - `STATUS === -1` (stopped): no-op (ticker may still invoke this).
|
|
225
261
|
* - `STATUS === 0` (pending): adopt `timestamp`, set `#delta = 0`, promote to started, then return (no work this frame).
|
|
226
262
|
* - `STATUS === 1` (started): normal tick.
|
|
@@ -239,9 +275,9 @@ class Thread {
|
|
|
239
275
|
}
|
|
240
276
|
Thread.#delta = (timestamp - Thread.#timestamp) / 1e3;
|
|
241
277
|
Thread.#timestamp = timestamp;
|
|
278
|
+
Thread.#DRIVERS_CALLSTACK.loop();
|
|
242
279
|
Thread.#execModifiers();
|
|
243
|
-
Thread.#
|
|
244
|
-
Thread.#execForeverTasks();
|
|
280
|
+
Thread.#execTasks();
|
|
245
281
|
Thread.#stack = null;
|
|
246
282
|
}
|
|
247
283
|
/**
|
|
@@ -254,7 +290,7 @@ class Thread {
|
|
|
254
290
|
}
|
|
255
291
|
// ##DESTROY
|
|
256
292
|
/**
|
|
257
|
-
* Clears every modifier and
|
|
293
|
+
* Clears every modifier, task, and driver callback.
|
|
258
294
|
*/
|
|
259
295
|
static #destroyVars() {
|
|
260
296
|
Thread.#modifiersBitMask = Thread.#flowBitMask = Thread.#foreverBitMask = 0;
|
|
@@ -263,6 +299,7 @@ class Thread {
|
|
|
263
299
|
if (Thread.#FOREVER_MAP.size) Thread.#FOREVER_MAP.clear();
|
|
264
300
|
if (Thread.#FLOW_LISTED.size) Thread.#FLOW_LISTED.clear();
|
|
265
301
|
if (Thread.#FOREVER_LISTED.size) Thread.#FOREVER_LISTED.clear();
|
|
302
|
+
Thread.#DRIVERS_CALLSTACK.clear();
|
|
266
303
|
}
|
|
267
304
|
/**
|
|
268
305
|
* Stops the internal `requestAnimationFrame` loop.
|
|
@@ -274,7 +311,7 @@ class Thread {
|
|
|
274
311
|
Thread.#tickID = -1;
|
|
275
312
|
}
|
|
276
313
|
/**
|
|
277
|
-
* Clears all modifiers and
|
|
314
|
+
* Clears all modifiers, tasks, and drivers, and stops the internal frame loop. Called from `Up.destroy` in the browser.
|
|
278
315
|
* @internal
|
|
279
316
|
*/
|
|
280
317
|
static [destroy]() {
|
|
@@ -350,88 +387,79 @@ class Thread {
|
|
|
350
387
|
Thread.#resolve(wrapper);
|
|
351
388
|
return 1;
|
|
352
389
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
static #execFlowTasks() {
|
|
357
|
-
if (!Thread.#flowBitMask) return;
|
|
390
|
+
static #execTasks() {
|
|
391
|
+
let bitMask = Thread.#foreverBitMask | Thread.#flowBitMask;
|
|
392
|
+
if (!bitMask) return;
|
|
358
393
|
const N1 = performance.now();
|
|
359
|
-
let
|
|
394
|
+
let foreverUsage = 0, foreverBitMask = Thread.#foreverBitMask, flowBitMask = Thread.#flowBitMask, n = 0, wrapper;
|
|
360
395
|
Thread.#flowBitMask = 0;
|
|
361
396
|
do {
|
|
362
397
|
const FLAG = bitMask & -bitMask;
|
|
363
398
|
bitMask ^= FLAG;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
399
|
+
if (foreverBitMask & FLAG) {
|
|
400
|
+
foreverBitMask ^= FLAG;
|
|
401
|
+
Thread.#stack = Thread.#FOREVER_MAP.get(FLAG);
|
|
402
|
+
Thread.#max = Thread.#stack.length;
|
|
403
|
+
if (Thread.#max) {
|
|
404
|
+
Thread.#i = 0;
|
|
405
|
+
const N2 = performance.now(), BUDGET = (Thread.#foreverBudget - N2 + N1) / (Thread.#getSize(foreverBitMask) + 1);
|
|
406
|
+
do {
|
|
407
|
+
Thread.#execTask(Thread.#stack[Thread.#i], BUDGET, N2);
|
|
408
|
+
} while (++Thread.#i < Thread.#max);
|
|
409
|
+
foreverUsage += performance.now() - N2;
|
|
410
|
+
} else Thread.#foreverBitMask ^= FLAG;
|
|
411
|
+
}
|
|
412
|
+
if (flowBitMask & FLAG) {
|
|
413
|
+
flowBitMask ^= FLAG;
|
|
414
|
+
Thread.#stack = Thread.#FLOW_MAP.get(FLAG);
|
|
415
|
+
Thread.#max = Thread.#stack.length;
|
|
416
|
+
if (!Thread.#max) continue;
|
|
417
|
+
Thread.#i = 0;
|
|
418
|
+
const N2 = n = performance.now(), BUDGET = (Thread.#flowBudget - N2 + N1 + foreverUsage) / (Thread.#getSize(flowBitMask) + 1);
|
|
419
|
+
FLOW_TASK_LOOP: do {
|
|
420
|
+
const R = Thread.#execTask(wrapper = Thread.#stack[Thread.#i], BUDGET, N2, n);
|
|
421
|
+
wrapper = Thread.#FLOW_LISTED.get(wrapper[0]);
|
|
422
|
+
if (wrapper && wrapper[1] === FLAG) switch (R) {
|
|
423
|
+
case 0:
|
|
424
|
+
break FLOW_TASK_LOOP;
|
|
425
|
+
case 1: {
|
|
388
426
|
if (wrapper[4]) {
|
|
389
427
|
wrapper[4] = false;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
else if (!STACK.includes(wrapper)) STACK.push(wrapper);
|
|
393
|
-
Thread.#flowBitMask |= wrapper[1];
|
|
428
|
+
Thread.#stack[Thread.#i] = [Thread.App.void, FLAG, []];
|
|
429
|
+
Thread.#stack.push(wrapper);
|
|
394
430
|
} else Thread.#FLOW_LISTED.delete(wrapper[0]);
|
|
395
|
-
|
|
431
|
+
continue FLOW_TASK_LOOP;
|
|
432
|
+
}
|
|
433
|
+
case 2: {
|
|
434
|
+
const TASK = wrapper[0];
|
|
435
|
+
wrapper[5].finally(() => {
|
|
436
|
+
wrapper = Thread.#FLOW_LISTED.get(TASK);
|
|
437
|
+
if (!wrapper) return;
|
|
438
|
+
if (wrapper[4]) {
|
|
439
|
+
wrapper[4] = false;
|
|
440
|
+
const STACK = Thread.#FLOW_MAP.get(wrapper[1]);
|
|
441
|
+
if (!STACK) Thread.#FLOW_MAP.set(wrapper[1], [wrapper]);
|
|
442
|
+
else if (!STACK.includes(wrapper)) STACK.push(wrapper);
|
|
443
|
+
Thread.#flowBitMask |= wrapper[1];
|
|
444
|
+
} else Thread.#FLOW_LISTED.delete(wrapper[0]);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
default:
|
|
448
|
+
continue FLOW_TASK_LOOP;
|
|
396
449
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
} while (++Thread.#i < Thread.#max && (n = performance.now()) - N2 < BUDGET);
|
|
405
|
-
Thread.#FLOW_MAP.set(FLAG, Thread.#stack = Thread.#stack.splice(Thread.#i));
|
|
406
|
-
if (Thread.#stack.length) Thread.#flowBitMask |= FLAG;
|
|
407
|
-
} while (bitMask);
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Runs every forever task by flag, sharing the forever budget between flags.
|
|
411
|
-
*/
|
|
412
|
-
static #execForeverTasks() {
|
|
413
|
-
if (!Thread.#foreverBitMask) return;
|
|
414
|
-
const N1 = performance.now();
|
|
415
|
-
let bitMask = Thread.#foreverBitMask;
|
|
416
|
-
do {
|
|
417
|
-
const FLAG = bitMask & -bitMask;
|
|
418
|
-
bitMask ^= FLAG;
|
|
419
|
-
Thread.#stack = Thread.#FOREVER_MAP.get(FLAG);
|
|
420
|
-
Thread.#max = Thread.#stack.length;
|
|
421
|
-
if (!Thread.#max) {
|
|
422
|
-
Thread.#foreverBitMask ^= FLAG;
|
|
423
|
-
continue;
|
|
450
|
+
else if (!R) {
|
|
451
|
+
++Thread.#i;
|
|
452
|
+
break FLOW_TASK_LOOP;
|
|
453
|
+
}
|
|
454
|
+
} while (++Thread.#i < Thread.#max && (n = performance.now()) - N2 < BUDGET);
|
|
455
|
+
Thread.#FLOW_MAP.set(FLAG, Thread.#stack = Thread.#stack.splice(Thread.#i));
|
|
456
|
+
if (Thread.#stack.length) Thread.#flowBitMask |= FLAG;
|
|
424
457
|
}
|
|
425
|
-
Thread.#i = 0;
|
|
426
|
-
const N2 = performance.now(), BUDGET = (Thread.#foreverBudget - N2 + N1) / (Thread.#getSize(bitMask) + 1);
|
|
427
|
-
do {
|
|
428
|
-
Thread.#execTask(Thread.#stack[Thread.#i], BUDGET, N2);
|
|
429
|
-
} while (++Thread.#i < Thread.#max);
|
|
430
458
|
} while (bitMask);
|
|
431
459
|
}
|
|
432
460
|
// ##UTILS
|
|
433
461
|
/**
|
|
434
|
-
* Throws if `flag` is not a power of 2 between `1` and
|
|
462
|
+
* Throws if `flag` is not a power of 2 between `1` and {@link Thread.MAX_FLAG}.
|
|
435
463
|
*/
|
|
436
464
|
static #checkFlag(flag) {
|
|
437
465
|
if (flag < 1 || flag > Thread.#MAX_FLAG || (flag & flag - 1) !== 0) Logs.throw("THREAD", `THE FLAG MUST BE A POWER OF 2 AND LESS THAN OR EQUAL TO ${Thread.#MAX_FLAG}.`);
|
|
@@ -491,9 +519,9 @@ class Thread {
|
|
|
491
519
|
* (the task stays scheduled until the promise settles).
|
|
492
520
|
* @template T Task type, used to type `args`.
|
|
493
521
|
* @param task The task function, identified by reference.
|
|
494
|
-
* @param flag Priority flag, a power of 2 from `1` to
|
|
522
|
+
* @param flag Priority flag, a power of 2 from `1` to {@link Thread.MAX_FLAG}. Default: {@link Thread.DEFAULT_FLAG} (`8192`).
|
|
495
523
|
* @param args Arguments passed to the task when it runs (the latest call wins).
|
|
496
|
-
* @throws If `flag` is not a power of 2 between `1` and `
|
|
524
|
+
* @throws If `flag` is not a power of 2 between `1` and {@link Thread.MAX_FLAG} (`32768`).
|
|
497
525
|
* @example
|
|
498
526
|
* ```ts
|
|
499
527
|
* function* build(items: HTMLElement[]): Generator<void, void, void>
|
|
@@ -518,7 +546,7 @@ class Thread {
|
|
|
518
546
|
Thread.#add(Thread.#FLOW_MAP, Thread.#FLOW_LISTED, task, FLAG, args);
|
|
519
547
|
Thread.#flowBitMask |= FLAG;
|
|
520
548
|
}
|
|
521
|
-
|
|
549
|
+
Thread.arm(FLAG);
|
|
522
550
|
}
|
|
523
551
|
/**
|
|
524
552
|
* Removes a scheduled task. A pending {@link Thread.wait} promise is resolved and a running generator is closed.
|
|
@@ -528,6 +556,20 @@ class Thread {
|
|
|
528
556
|
static unsub(task) {
|
|
529
557
|
task[FOREVER] ? Thread.#remove(Thread.#FOREVER_MAP, Thread.#FOREVER_LISTED, task) : Thread.#remove(Thread.#FLOW_MAP, Thread.#FLOW_LISTED, task);
|
|
530
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* Arms modifiers of `flag` for the next modifiers phase (same frame if called from a driver or before modifiers run).
|
|
561
|
+
* No-op when no modifiers are registered on `flag`. {@link Thread.sub} calls this automatically; call it yourself
|
|
562
|
+
* when an external writer updates shared state without scheduling a task (e.g. Scroll arms {@link Thread.SCROLL_FLAG}
|
|
563
|
+
* after Lenis writes `window.scrollY`, so `App.updateScrollVars` and ScrollTrigger modifiers run).
|
|
564
|
+
* @param flag Priority flag whose modifiers should run.
|
|
565
|
+
* @example
|
|
566
|
+
* ```ts
|
|
567
|
+
* lenis.on('scroll', () => Up.Thread.arm(32))
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
static arm(flag) {
|
|
571
|
+
if (Thread.#MODIFIERS_MAP.has(flag)) Thread.#modifiersBitMask |= flag;
|
|
572
|
+
}
|
|
531
573
|
/**
|
|
532
574
|
* Returns a promise that resolves when a scheduled task completes its next (or current) run, or when it is unsubscribed.
|
|
533
575
|
* @param task The same function reference passed to {@link Thread.sub}.
|