phase 0.0.1-alpha.1 → 0.0.2
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/LICENSE +21 -0
- package/README.md +917 -0
- package/dist/ease.d.ts +34 -0
- package/dist/ease.d.ts.map +1 -0
- package/dist/ease.js +56 -0
- package/dist/ease.js.map +1 -0
- package/dist/index-D3epuj2x.d.ts +249 -0
- package/dist/index-D3epuj2x.d.ts.map +1 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +702 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +1066 -0
- package/dist/react.js.map +1 -0
- package/dist/reduced-motion-CEJtegNG.js +948 -0
- package/dist/reduced-motion-CEJtegNG.js.map +1 -0
- package/package.json +78 -17
- package/index.js +0 -24
package/dist/ease.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/ease/index.d.ts
|
|
2
|
+
/** Cubic ease-out: decelerates to zero velocity. */
|
|
3
|
+
declare function easeOutCubic(progress: number): number;
|
|
4
|
+
/** Quartic ease-out: sharper deceleration than cubic. */
|
|
5
|
+
declare function easeOutQuart(progress: number): number;
|
|
6
|
+
/**
|
|
7
|
+
* Ease-out with overshoot (elastic snap-back).
|
|
8
|
+
* @param overshoot - Controls how far past the target the animation goes. Default 1.70158 (≈10% overshoot).
|
|
9
|
+
*/
|
|
10
|
+
declare function easeOutBack(progress: number, overshoot?: number): number;
|
|
11
|
+
/** Cubic ease-in-out: accelerates then decelerates symmetrically. */
|
|
12
|
+
declare function easeInOutCubic(progress: number): number;
|
|
13
|
+
/** Linear (identity). No easing applied. */
|
|
14
|
+
declare function linear(progress: number): number;
|
|
15
|
+
/** Clamp a value between min and max. */
|
|
16
|
+
declare function clamp(value: number, min: number, max: number): number;
|
|
17
|
+
/** Clamp a value to the 0–1 range. */
|
|
18
|
+
declare function clamp01(value: number): number;
|
|
19
|
+
/** Linear interpolation between start and end by progress (0–1). */
|
|
20
|
+
declare function lerp(start: number, end: number, progress: number): number;
|
|
21
|
+
/** Inverse of lerp. Returns the progress (0–1) for a given value between start and end. */
|
|
22
|
+
declare function inverseLerp(start: number, end: number, value: number): number;
|
|
23
|
+
interface RemapOptions {
|
|
24
|
+
inMin: number;
|
|
25
|
+
inMax: number;
|
|
26
|
+
outMin: number;
|
|
27
|
+
outMax: number;
|
|
28
|
+
value: number;
|
|
29
|
+
}
|
|
30
|
+
/** Map a value from one range to another. */
|
|
31
|
+
declare function remap(options: RemapOptions): number;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { RemapOptions, clamp, clamp01, easeInOutCubic, easeOutBack, easeOutCubic, easeOutQuart, inverseLerp, lerp, linear, remap };
|
|
34
|
+
//# sourceMappingURL=ease.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ease.d.ts","names":[],"sources":["../src/ease/index.ts"],"mappings":";;iBACgB,YAAA,CAAa,QAAA;;iBAMb,YAAA,CAAa,QAAA;;;AAA7B;;iBASgB,WAAA,CAAY,QAAA,UAAkB,SAAA;;iBAM9B,cAAA,CAAe,QAAA;AAN/B;AAAA,iBAagB,MAAA,CAAO,QAAA;;iBAKP,KAAA,CAAM,KAAA,UAAe,GAAA,UAAa,GAAA;;iBAOlC,OAAA,CAAQ,KAAA;;iBAOR,IAAA,CAAK,KAAA,UAAe,GAAA,UAAa,QAAA;;iBAKjC,WAAA,CAAY,KAAA,UAAe,GAAA,UAAa,KAAA;AAAA,UAIvC,YAAA;EACf,KAAA;EACA,KAAA;EACA,MAAA;EACA,MAAA;EACA,KAAA;AAAA;;iBAIc,KAAA,CAAM,OAAA,EAAS,YAAA"}
|
package/dist/ease.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/ease/index.ts
|
|
2
|
+
/** Cubic ease-out: decelerates to zero velocity. */
|
|
3
|
+
function easeOutCubic(progress) {
|
|
4
|
+
const inv = progress - 1;
|
|
5
|
+
return inv * inv * inv + 1;
|
|
6
|
+
}
|
|
7
|
+
/** Quartic ease-out: sharper deceleration than cubic. */
|
|
8
|
+
function easeOutQuart(progress) {
|
|
9
|
+
const inv = progress - 1;
|
|
10
|
+
return 1 - inv * inv * inv * inv;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Ease-out with overshoot (elastic snap-back).
|
|
14
|
+
* @param overshoot - Controls how far past the target the animation goes. Default 1.70158 (≈10% overshoot).
|
|
15
|
+
*/
|
|
16
|
+
function easeOutBack(progress, overshoot = 1.70158) {
|
|
17
|
+
const inv = progress - 1;
|
|
18
|
+
return inv * inv * ((overshoot + 1) * inv + overshoot) + 1;
|
|
19
|
+
}
|
|
20
|
+
/** Cubic ease-in-out: accelerates then decelerates symmetrically. */
|
|
21
|
+
function easeInOutCubic(progress) {
|
|
22
|
+
return progress < .5 ? 4 * progress * progress * progress : 1 - (-2 * progress + 2) ** 3 / 2;
|
|
23
|
+
}
|
|
24
|
+
/** Linear (identity). No easing applied. */
|
|
25
|
+
function linear(progress) {
|
|
26
|
+
return progress;
|
|
27
|
+
}
|
|
28
|
+
/** Clamp a value between min and max. */
|
|
29
|
+
function clamp(value, min, max) {
|
|
30
|
+
if (value < min) return min;
|
|
31
|
+
if (value > max) return max;
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
/** Clamp a value to the 0–1 range. */
|
|
35
|
+
function clamp01(value) {
|
|
36
|
+
if (value < 0) return 0;
|
|
37
|
+
if (value > 1) return 1;
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
/** Linear interpolation between start and end by progress (0–1). */
|
|
41
|
+
function lerp(start, end, progress) {
|
|
42
|
+
return start + (end - start) * progress;
|
|
43
|
+
}
|
|
44
|
+
/** Inverse of lerp. Returns the progress (0–1) for a given value between start and end. */
|
|
45
|
+
function inverseLerp(start, end, value) {
|
|
46
|
+
return start === end ? 0 : (value - start) / (end - start);
|
|
47
|
+
}
|
|
48
|
+
/** Map a value from one range to another. */
|
|
49
|
+
function remap(options) {
|
|
50
|
+
const progress = inverseLerp(options.inMin, options.inMax, options.value);
|
|
51
|
+
return lerp(options.outMin, options.outMax, progress);
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { clamp, clamp01, easeInOutCubic, easeOutBack, easeOutCubic, easeOutQuart, inverseLerp, lerp, linear, remap };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=ease.js.map
|
package/dist/ease.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ease.js","names":[],"sources":["../src/ease/index.ts"],"sourcesContent":["/** Cubic ease-out: decelerates to zero velocity. */\nexport function easeOutCubic(progress: number): number {\n const inv = progress - 1;\n return inv * inv * inv + 1;\n}\n\n/** Quartic ease-out: sharper deceleration than cubic. */\nexport function easeOutQuart(progress: number): number {\n const inv = progress - 1;\n return 1 - inv * inv * inv * inv;\n}\n\n/**\n * Ease-out with overshoot (elastic snap-back).\n * @param overshoot - Controls how far past the target the animation goes. Default 1.70158 (≈10% overshoot).\n */\nexport function easeOutBack(progress: number, overshoot = 1.70158): number {\n const inv = progress - 1;\n return inv * inv * ((overshoot + 1) * inv + overshoot) + 1;\n}\n\n/** Cubic ease-in-out: accelerates then decelerates symmetrically. */\nexport function easeInOutCubic(progress: number): number {\n return progress < 0.5\n ? 4 * progress * progress * progress\n : 1 - (-2 * progress + 2) ** 3 / 2;\n}\n\n/** Linear (identity). No easing applied. */\nexport function linear(progress: number): number {\n return progress;\n}\n\n/** Clamp a value between min and max. */\nexport function clamp(value: number, min: number, max: number): number {\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\n/** Clamp a value to the 0–1 range. */\nexport function clamp01(value: number): number {\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\n/** Linear interpolation between start and end by progress (0–1). */\nexport function lerp(start: number, end: number, progress: number): number {\n return start + (end - start) * progress;\n}\n\n/** Inverse of lerp. Returns the progress (0–1) for a given value between start and end. */\nexport function inverseLerp(start: number, end: number, value: number): number {\n return start === end ? 0 : (value - start) / (end - start);\n}\n\nexport interface RemapOptions {\n inMin: number;\n inMax: number;\n outMin: number;\n outMax: number;\n value: number;\n}\n\n/** Map a value from one range to another. */\nexport function remap(options: RemapOptions): number {\n const progress = inverseLerp(options.inMin, options.inMax, options.value);\n return lerp(options.outMin, options.outMax, progress);\n}\n"],"mappings":";;AACA,SAAgB,aAAa,UAA0B;CACrD,MAAM,MAAM,WAAW;AACvB,QAAO,MAAM,MAAM,MAAM;;;AAI3B,SAAgB,aAAa,UAA0B;CACrD,MAAM,MAAM,WAAW;AACvB,QAAO,IAAI,MAAM,MAAM,MAAM;;;;;;AAO/B,SAAgB,YAAY,UAAkB,YAAY,SAAiB;CACzE,MAAM,MAAM,WAAW;AACvB,QAAO,MAAM,QAAQ,YAAY,KAAK,MAAM,aAAa;;;AAI3D,SAAgB,eAAe,UAA0B;AACvD,QAAO,WAAW,KACd,IAAI,WAAW,WAAW,WAC1B,KAAK,KAAK,WAAW,MAAM,IAAI;;;AAIrC,SAAgB,OAAO,UAA0B;AAC/C,QAAO;;;AAIT,SAAgB,MAAM,OAAe,KAAa,KAAqB;AACrE,KAAI,QAAQ,IAAK,QAAO;AACxB,KAAI,QAAQ,IAAK,QAAO;AACxB,QAAO;;;AAIT,SAAgB,QAAQ,OAAuB;AAC7C,KAAI,QAAQ,EAAG,QAAO;AACtB,KAAI,QAAQ,EAAG,QAAO;AACtB,QAAO;;;AAIT,SAAgB,KAAK,OAAe,KAAa,UAA0B;AACzE,QAAO,SAAS,MAAM,SAAS;;;AAIjC,SAAgB,YAAY,OAAe,KAAa,OAAuB;AAC7E,QAAO,UAAU,MAAM,KAAK,QAAQ,UAAU,MAAM;;;AAYtD,SAAgB,MAAM,SAA+B;CACnD,MAAM,WAAW,YAAY,QAAQ,OAAO,QAAQ,OAAO,QAAQ,MAAM;AACzE,QAAO,KAAK,QAAQ,QAAQ,QAAQ,QAAQ,SAAS"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
//#region src/core/tick/index.d.ts
|
|
2
|
+
interface FrameState {
|
|
3
|
+
/** Current timestamp from performance.now(). */
|
|
4
|
+
time: number;
|
|
5
|
+
/** Milliseconds since last tick, clamped to 40ms. */
|
|
6
|
+
delta: number;
|
|
7
|
+
/** Milliseconds since start, excluding paused time. */
|
|
8
|
+
elapsed: number;
|
|
9
|
+
/** Frame count since start. */
|
|
10
|
+
frame: number;
|
|
11
|
+
}
|
|
12
|
+
type TickerPhase = 'idle' | 'running' | 'paused' | 'stopped';
|
|
13
|
+
type TickerReason = 'initial' | 'started' | 'resumed' | 'manual' | 'disposed';
|
|
14
|
+
interface TickerOptions {
|
|
15
|
+
/** Cap frame rate. Default: uncapped (display refresh rate). */
|
|
16
|
+
fps?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Called every frame with the current frame state. Write to refs or DOM
|
|
19
|
+
* directly. Never call React `setState` here (60 state updates/sec = 60 re-renders/sec).
|
|
20
|
+
*/
|
|
21
|
+
onTick: (frame: FrameState) => void;
|
|
22
|
+
/** Abort signal that stops the ticker when aborted. */
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
interface Ticker {
|
|
26
|
+
start(): void;
|
|
27
|
+
stop(): void;
|
|
28
|
+
pause(): void;
|
|
29
|
+
resume(): void;
|
|
30
|
+
readonly phase: TickerPhase;
|
|
31
|
+
readonly phaseReason: TickerReason;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Core rAF loop primitive with FPS cap, delta clamping, and strong pause.
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* `FrameState` is reused across frames. Do not store a reference to it.
|
|
38
|
+
* Read values immediately in your `onTick` callback.
|
|
39
|
+
*/
|
|
40
|
+
declare function createTicker(options: TickerOptions): Ticker;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/core/sight/index.d.ts
|
|
43
|
+
type SightPhase = 'unknown' | 'visible' | 'hidden';
|
|
44
|
+
type SightReason = 'initial' | 'viewport' | 'document' | 'bfcache' | 'all-hidden';
|
|
45
|
+
interface SightOptions {
|
|
46
|
+
element: Element;
|
|
47
|
+
intersectionOptions?: IntersectionObserverInit;
|
|
48
|
+
onPhaseChange?: (phase: SightPhase, reason: SightReason) => void;
|
|
49
|
+
/** Abort signal that stops the observer when aborted. */
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
}
|
|
52
|
+
interface Sight {
|
|
53
|
+
readonly phase: SightPhase;
|
|
54
|
+
readonly phaseReason: SightReason;
|
|
55
|
+
stop(): void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Visibility observer combining document focus and viewport intersection.
|
|
59
|
+
*
|
|
60
|
+
* `phase` is `'visible'` only when both the document is visible (not backgrounded)
|
|
61
|
+
* and the element is within the viewport. Uses a shared IntersectionObserver
|
|
62
|
+
* pool. Multiple `createSight` calls with the same options share one observer.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const sight = createSight({
|
|
66
|
+
* element: el,
|
|
67
|
+
* onPhaseChange: (phase) => phase === 'visible' ? loop.start() : loop.pause(),
|
|
68
|
+
* });
|
|
69
|
+
* // cleanup:
|
|
70
|
+
* sight.stop();
|
|
71
|
+
*
|
|
72
|
+
* @remarks
|
|
73
|
+
* `onPhaseChange` fires only on phase transitions, not on every IntersectionObserver callback.
|
|
74
|
+
*/
|
|
75
|
+
declare function createSight(options: SightOptions): Sight;
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/core/lifecycle/index.d.ts
|
|
78
|
+
type LifecyclePhase = 'idle' | 'active' | 'paused' | 'stopped';
|
|
79
|
+
type LifecycleReason = 'initial' | 'started' | 'resumed' | 'sight' | 'reduced-motion' | 'manual' | 'disposed';
|
|
80
|
+
/** Whether reduced motion pauses the lifecycle. Default `'pause'`. */
|
|
81
|
+
type LifecycleReducedMotion = 'pause' | 'ignore';
|
|
82
|
+
interface LifecycleOptions {
|
|
83
|
+
element: Element;
|
|
84
|
+
reducedMotion?: LifecycleReducedMotion;
|
|
85
|
+
intersectionOptions?: IntersectionObserverInit;
|
|
86
|
+
start?: 'auto' | 'manual';
|
|
87
|
+
onPhaseChange?: (phase: LifecyclePhase, reason: LifecycleReason) => void;
|
|
88
|
+
/** Abort signal that stops the lifecycle when aborted. */
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
}
|
|
91
|
+
interface Lifecycle {
|
|
92
|
+
/** Begin honoring signals. Called automatically unless `start: 'manual'`. */
|
|
93
|
+
start(): void;
|
|
94
|
+
/** Terminal. Disposes observers and listeners. Cannot be restarted. */
|
|
95
|
+
stop(): void;
|
|
96
|
+
/** Manually pause (e.g. a panel opened over the animation). Lowest priority. */
|
|
97
|
+
pause(): void;
|
|
98
|
+
/** Clear a manual pause. */
|
|
99
|
+
resume(): void;
|
|
100
|
+
readonly phase: LifecyclePhase;
|
|
101
|
+
readonly phaseReason: LifecycleReason;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The activation decision for an animation, decoupled from who drives the frames.
|
|
105
|
+
*
|
|
106
|
+
* Composes visibility (`createSight`), reduced motion, and a manual pause into a
|
|
107
|
+
* single `active` / `paused` phase. Use when you own your render loop (WebGL,
|
|
108
|
+
* three.js, a Web Worker, or non-rAF work that should still pause off-screen or
|
|
109
|
+
* under reduced motion). For loops `phase` should drive, use `createLoop` instead.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* const lifecycle = createLifecycle({
|
|
113
|
+
* element: canvas,
|
|
114
|
+
* onPhaseChange: (phase) => {
|
|
115
|
+
* if (phase === 'active') renderer.start();
|
|
116
|
+
* else renderer.stop();
|
|
117
|
+
* },
|
|
118
|
+
* });
|
|
119
|
+
* // cleanup:
|
|
120
|
+
* lifecycle.stop();
|
|
121
|
+
*/
|
|
122
|
+
declare function createLifecycle(options: LifecycleOptions): Lifecycle;
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/core/loop/index.d.ts
|
|
125
|
+
type ReducedMotionBehavior = 'pause' | 'complete' | 'ignore';
|
|
126
|
+
type DegradedBehavior = 'throttle' | 'pause' | 'ignore';
|
|
127
|
+
type LoopPhase = 'idle' | 'running' | 'paused' | 'stopped';
|
|
128
|
+
type LoopReason = 'initial' | 'started' | 'resumed' | 'sight' | 'reduced-motion' | 'degraded' | 'manual' | 'disposed';
|
|
129
|
+
type Quality = 'full' | 'degraded';
|
|
130
|
+
type DegradedReason = 'unfocused' | 'frame-budget';
|
|
131
|
+
interface LoopOptionsBase {
|
|
132
|
+
element: Element;
|
|
133
|
+
/**
|
|
134
|
+
* Called every frame. Write to refs or DOM directly. Never call React
|
|
135
|
+
* `setState` here (60 calls/sec = 60 re-renders/sec).
|
|
136
|
+
*/
|
|
137
|
+
onTick: (frame: FrameState) => void;
|
|
138
|
+
fps?: number;
|
|
139
|
+
reducedMotion?: ReducedMotionBehavior;
|
|
140
|
+
intersectionOptions?: IntersectionObserverInit;
|
|
141
|
+
start?: 'auto' | 'manual';
|
|
142
|
+
onPhaseChange?: (phase: LoopPhase, reason: LoopReason) => void;
|
|
143
|
+
/** Abort signal that stops the loop when aborted. */
|
|
144
|
+
signal?: AbortSignal;
|
|
145
|
+
}
|
|
146
|
+
type DegradedOptions = {
|
|
147
|
+
degraded?: 'throttle';
|
|
148
|
+
degradedFps?: number;
|
|
149
|
+
} | {
|
|
150
|
+
degraded: 'pause';
|
|
151
|
+
} | {
|
|
152
|
+
degraded: 'ignore';
|
|
153
|
+
};
|
|
154
|
+
type LoopOptions = LoopOptionsBase & DegradedOptions;
|
|
155
|
+
interface Loop {
|
|
156
|
+
start(): void;
|
|
157
|
+
stop(): void;
|
|
158
|
+
readonly phase: LoopPhase;
|
|
159
|
+
readonly phaseReason: LoopReason;
|
|
160
|
+
readonly quality: Quality;
|
|
161
|
+
readonly qualityReason: DegradedReason | undefined;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Lifecycle-aware animation loop composing ticker, visibility, and reduced motion.
|
|
165
|
+
*
|
|
166
|
+
* Pass an element and get a loop that pauses when the element leaves the viewport
|
|
167
|
+
* or the tab is backgrounded, resumes when it returns, and cleans up with `stop()`.
|
|
168
|
+
*
|
|
169
|
+
* @remarks
|
|
170
|
+
* The loop is signal-driven and exposes only `start()` and `stop()`. There is no
|
|
171
|
+
* imperative `pause()`/`resume()`. Pausing is decided by visibility, reduced
|
|
172
|
+
* motion, and quality, so an imperative pause would compete with those signals.
|
|
173
|
+
* For manual control, use `useLoop`'s `enabled` option (React) or `createLifecycle`,
|
|
174
|
+
* which exposes `pause()`/`resume()` for loops you drive yourself.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* const loop = createLoop({
|
|
178
|
+
* element: el,
|
|
179
|
+
* onTick: (frame) => draw(ctx, frame),
|
|
180
|
+
* });
|
|
181
|
+
* // cleanup:
|
|
182
|
+
* loop.stop();
|
|
183
|
+
*/
|
|
184
|
+
declare function createLoop(options: LoopOptions): Loop;
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/core/render-state/index.d.ts
|
|
187
|
+
type RenderPhase = 'rendered' | 'skipped';
|
|
188
|
+
interface RenderStateOptions {
|
|
189
|
+
element: Element;
|
|
190
|
+
onPhaseChange?: (phase: RenderPhase) => void;
|
|
191
|
+
/** Abort signal that stops the observer when aborted. */
|
|
192
|
+
signal?: AbortSignal;
|
|
193
|
+
}
|
|
194
|
+
interface RenderState {
|
|
195
|
+
/** Whether the browser is currently rendering the element or skipping it. */
|
|
196
|
+
readonly phase: RenderPhase;
|
|
197
|
+
stop(): void;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Report whether the browser is rendering an element or skipping it under
|
|
201
|
+
* `content-visibility`. Listens to the `contentvisibilityautostatechange`
|
|
202
|
+
* event, the browser's ground-truth paint decision.
|
|
203
|
+
*
|
|
204
|
+
* Use it to pause raw, non-phase work (a hand-written rAF loop, `setInterval`,
|
|
205
|
+
* expensive effects) when a `Defer` subtree stops painting. phase's own loops
|
|
206
|
+
* already self-pause off-screen, so they do not need this.
|
|
207
|
+
*
|
|
208
|
+
* Listening and reacting has zero layout effect. It never breaks the
|
|
209
|
+
* no-layout-shift guarantee of `content-visibility`.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* const render = createRenderState({
|
|
213
|
+
* element: el,
|
|
214
|
+
* onPhaseChange: (phase) => phase === 'skipped' ? clock.pause() : clock.resume(),
|
|
215
|
+
* });
|
|
216
|
+
* // cleanup:
|
|
217
|
+
* render.stop();
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* Where `content-visibility` is unsupported, `phase` stays `'rendered'`.
|
|
221
|
+
*
|
|
222
|
+
* Per the CSS Containment spec, `ResizeObserver` callbacks pause for elements
|
|
223
|
+
* inside a skipped `content-visibility: auto` subtree. Use this primitive to
|
|
224
|
+
* detect that transition when your code depends on size observations resuming.
|
|
225
|
+
*/
|
|
226
|
+
declare function createRenderState(options: RenderStateOptions): RenderState;
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/core/idle/index.d.ts
|
|
229
|
+
interface IdleOptions {
|
|
230
|
+
/** Max ms to wait before running the callback even if no idle period occurs. */
|
|
231
|
+
timeout?: number;
|
|
232
|
+
/** Abort signal that cancels the scheduled callback when aborted. */
|
|
233
|
+
signal?: AbortSignal;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Run a callback once the browser is idle. Wraps `requestIdleCallback`, falling
|
|
237
|
+
* back to a near-immediate `setTimeout` where it is unavailable (Safari).
|
|
238
|
+
*
|
|
239
|
+
* Returns a cancel function. Calling it before the callback runs prevents it.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* const cancel = whenIdle(() => warmCache(), { timeout: 2000 });
|
|
243
|
+
* // later, if no longer needed:
|
|
244
|
+
* cancel();
|
|
245
|
+
*/
|
|
246
|
+
declare function whenIdle(callback: () => void, options?: IdleOptions): () => void;
|
|
247
|
+
//#endregion
|
|
248
|
+
export { TickerPhase as A, SightOptions as C, FrameState as D, createSight as E, createTicker as M, Ticker as O, Sight as S, SightReason as T, LifecycleOptions as _, RenderStateOptions as a, LifecycleReducedMotion as b, DegradedReason as c, LoopPhase as d, LoopReason as f, Lifecycle as g, createLoop as h, RenderState as i, TickerReason as j, TickerOptions as k, Loop as l, ReducedMotionBehavior as m, whenIdle as n, createRenderState as o, Quality as p, RenderPhase as r, DegradedBehavior as s, IdleOptions as t, LoopOptions as u, LifecyclePhase as v, SightPhase as w, createLifecycle as x, LifecycleReason as y };
|
|
249
|
+
//# sourceMappingURL=index-D3epuj2x.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-D3epuj2x.d.ts","names":[],"sources":["../src/core/tick/index.ts","../src/core/sight/index.ts","../src/core/lifecycle/index.ts","../src/core/loop/index.ts","../src/core/render-state/index.ts","../src/core/idle/index.ts"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { RemapOptions, clamp, clamp01, easeInOutCubic, easeOutBack, easeOutCubic, easeOutQuart, inverseLerp, lerp, linear, remap } from "./ease.js";
|
|
2
|
+
import { A as TickerPhase, C as SightOptions, D as FrameState, E as createSight, M as createTicker, O as Ticker, S as Sight, T as SightReason, _ as LifecycleOptions, a as RenderStateOptions, b as LifecycleReducedMotion, c as DegradedReason, d as LoopPhase, f as LoopReason, g as Lifecycle, h as createLoop, i as RenderState, j as TickerReason, k as TickerOptions, l as Loop, m as ReducedMotionBehavior, n as whenIdle, o as createRenderState, p as Quality, r as RenderPhase, s as DegradedBehavior, t as IdleOptions, u as LoopOptions, v as LifecyclePhase, w as SightPhase, x as createLifecycle, y as LifecycleReason } from "./index-D3epuj2x.js";
|
|
3
|
+
|
|
4
|
+
//#region src/core/scroll-progress/index.d.ts
|
|
5
|
+
interface ScrollProgressOptions {
|
|
6
|
+
element: Element;
|
|
7
|
+
/** Called when the intersection ratio changes at a threshold crossing. */
|
|
8
|
+
onProgress: (ratio: number) => void;
|
|
9
|
+
/** Number of evenly-spaced thresholds. Default 20 (~5% granularity). */
|
|
10
|
+
steps?: number;
|
|
11
|
+
root?: Element | Document | null;
|
|
12
|
+
rootMargin?: string;
|
|
13
|
+
/** Abort signal that stops the observer when aborted. */
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
interface ScrollProgress {
|
|
17
|
+
/** Current intersection ratio (0–1). Synchronous read of the last-reported value. */
|
|
18
|
+
readonly ratio: number;
|
|
19
|
+
stop(): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Observe what fraction of an element is visible in the viewport (0–1).
|
|
23
|
+
*
|
|
24
|
+
* Uses the shared IntersectionObserver pool with multi-threshold options.
|
|
25
|
+
* Multiple instances with the same `steps` share a single IO.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const progress = createScrollProgress({
|
|
29
|
+
* element: el,
|
|
30
|
+
* onProgress: (ratio) => {
|
|
31
|
+
* el.style.opacity = String(ratio);
|
|
32
|
+
* },
|
|
33
|
+
* });
|
|
34
|
+
* // cleanup:
|
|
35
|
+
* progress.stop();
|
|
36
|
+
*/
|
|
37
|
+
declare function createScrollProgress(options: ScrollProgressOptions): ScrollProgress;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/core/device-pixel-ratio/index.d.ts
|
|
40
|
+
interface DevicePixelRatioOptions {
|
|
41
|
+
/** Called when devicePixelRatio changes (e.g. window moved between monitors). */
|
|
42
|
+
onChange: (dpr: number) => void;
|
|
43
|
+
/** Abort signal that stops the watcher when aborted. */
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
interface DevicePixelRatio {
|
|
47
|
+
/** Current devicePixelRatio. Synchronous read of the last-reported value. */
|
|
48
|
+
readonly dpr: number;
|
|
49
|
+
stop(): void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Track devicePixelRatio changes (e.g. user drags the window between monitors
|
|
53
|
+
* with different pixel densities).
|
|
54
|
+
*
|
|
55
|
+
* Uses a shared `matchMedia` subscription that re-subscribes on every change,
|
|
56
|
+
* so chained monitor switches (A -> B -> C) are all caught. Multiple instances
|
|
57
|
+
* share one underlying subscription.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* const watcher = createDevicePixelRatio({
|
|
61
|
+
* onChange: (dpr) => bridge.setDpr(dpr),
|
|
62
|
+
* });
|
|
63
|
+
* watcher.dpr; // current value
|
|
64
|
+
* // cleanup:
|
|
65
|
+
* watcher.stop();
|
|
66
|
+
*/
|
|
67
|
+
declare function createDevicePixelRatio(options: DevicePixelRatioOptions): DevicePixelRatio;
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/core/reduced-motion/index.d.ts
|
|
70
|
+
/**
|
|
71
|
+
* Synchronous check for `prefers-reduced-motion: reduce`.
|
|
72
|
+
*
|
|
73
|
+
* Returns `false` on the server (no `matchMedia`). On the client, reads from
|
|
74
|
+
* the shared MQL pool so the underlying `MediaQueryList` is reused across
|
|
75
|
+
* all callers.
|
|
76
|
+
*/
|
|
77
|
+
declare function prefersReducedMotion(): boolean;
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/core/_internal/errors/index.d.ts
|
|
80
|
+
type PhaseErrorCode = 'server_context' | 'no_element' | 'invalid_duration' | 'ticker_stopped' | 'missing_context';
|
|
81
|
+
interface PhaseErrorOptions {
|
|
82
|
+
code: PhaseErrorCode;
|
|
83
|
+
reason?: string;
|
|
84
|
+
fix?: string;
|
|
85
|
+
link?: string;
|
|
86
|
+
}
|
|
87
|
+
/** Lightweight structured error for phase. */
|
|
88
|
+
declare class PhaseError extends Error {
|
|
89
|
+
readonly code: PhaseErrorCode;
|
|
90
|
+
readonly reason: string | undefined;
|
|
91
|
+
readonly fix: string | undefined;
|
|
92
|
+
readonly link: string | undefined;
|
|
93
|
+
constructor(message: string, options: PhaseErrorOptions);
|
|
94
|
+
}
|
|
95
|
+
/** Check if a value is a PhaseError instance. */
|
|
96
|
+
declare function isPhaseError(error: unknown): error is PhaseError;
|
|
97
|
+
//#endregion
|
|
98
|
+
export { type DegradedBehavior, type DegradedReason, type DevicePixelRatio, type DevicePixelRatioOptions, type FrameState, type IdleOptions, type Lifecycle, type LifecycleOptions, type LifecyclePhase, type LifecycleReason, type LifecycleReducedMotion, type Loop, type LoopOptions, type LoopPhase, type LoopReason, PhaseError, type PhaseErrorCode, type Quality, type ReducedMotionBehavior, type RemapOptions, type RenderPhase, type RenderState, type RenderStateOptions, type ScrollProgress, type ScrollProgressOptions, type Sight, type SightOptions, type SightPhase, type SightReason, type Ticker, type TickerOptions, type TickerPhase, type TickerReason, clamp, clamp01, createDevicePixelRatio, createLifecycle, createLoop, createRenderState, createScrollProgress, createSight, createTicker, easeInOutCubic, easeOutBack, easeOutCubic, easeOutQuart, inverseLerp, isPhaseError, lerp, linear, prefersReducedMotion, remap, whenIdle };
|
|
99
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/core/scroll-progress/index.ts","../src/core/device-pixel-ratio/index.ts","../src/core/reduced-motion/index.ts","../src/core/_internal/errors/index.ts"],"mappings":";;;;UAQiB,qBAAA;EACf,OAAA,EAAS,OAAA;;EAET,UAAA,GAAa,KAAA;EAHE;EAKf,KAAA;EACA,IAAA,GAAO,OAAA,GAAU,QAAA;EACjB,UAAA;EADO;EAGP,MAAA,GAAS,WAAA;AAAA;AAAA,UAGM,cAAA;EAHK;EAAA,SAKX,KAAA;EACT,IAAA;AAAA;;;;;;;;;;;AAHF;;;;;AAqDA;iBAAgB,oBAAA,CACd,OAAA,EAAS,qBAAA,GACR,cAAA;;;UCnEc,uBAAA;;EAEf,QAAA,GAAW,GAAA;;EAEX,MAAA,GAAS,WAAA;AAAA;AAAA,UAGM,gBAAA;EDNN;EAAA,SCQA,GAAA;EACT,IAAA;AAAA;;;;;;;;;;;;;;;;ADEF;iBCqBgB,sBAAA,CACd,OAAA,EAAS,uBAAA,GACR,gBAAA;;;;;;ADnCH;;;;iBEGgB,oBAAA,CAAA;;;KCXJ,cAAA;AAAA,UAOF,iBAAA;EACR,IAAA,EAAM,cAAA;EACN,MAAA;EACA,GAAA;EACA,IAAA;AAAA;;cAIW,UAAA,SAAmB,KAAA;EAAA,SACrB,IAAA,EAAM,cAAA;EAAA,SACN,MAAA;EAAA,SACA,GAAA;EAAA,SACA,IAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,iBAAA;AAAA;;iBAWxB,YAAA,CAAa,KAAA,YAAiB,KAAA,IAAS,UAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { clamp, clamp01, easeInOutCubic, easeOutBack, easeOutCubic, easeOutQuart, inverseLerp, lerp, linear, remap } from "./ease.js";
|
|
2
|
+
import { a as subscribeDpr, c as createLoop, f as createSight, g as isPhaseError, i as readDpr, l as createLifecycle, m as PhaseError, n as prefersReducedMotion, o as createRenderState, p as createTicker, r as whenIdle, s as createScrollProgress, v as serverContextError, y as linkAbortSignal } from "./reduced-motion-CEJtegNG.js";
|
|
3
|
+
//#region src/core/device-pixel-ratio/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* Track devicePixelRatio changes (e.g. user drags the window between monitors
|
|
6
|
+
* with different pixel densities).
|
|
7
|
+
*
|
|
8
|
+
* Uses a shared `matchMedia` subscription that re-subscribes on every change,
|
|
9
|
+
* so chained monitor switches (A -> B -> C) are all caught. Multiple instances
|
|
10
|
+
* share one underlying subscription.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const watcher = createDevicePixelRatio({
|
|
14
|
+
* onChange: (dpr) => bridge.setDpr(dpr),
|
|
15
|
+
* });
|
|
16
|
+
* watcher.dpr; // current value
|
|
17
|
+
* // cleanup:
|
|
18
|
+
* watcher.stop();
|
|
19
|
+
*/
|
|
20
|
+
function createDevicePixelRatio(options) {
|
|
21
|
+
if (typeof matchMedia === "undefined") serverContextError("createDevicePixelRatio");
|
|
22
|
+
const { onChange, signal } = options;
|
|
23
|
+
let _dpr = readDpr();
|
|
24
|
+
let stopped = false;
|
|
25
|
+
const unsubscribe = subscribeDpr((dpr) => {
|
|
26
|
+
_dpr = dpr;
|
|
27
|
+
onChange(dpr);
|
|
28
|
+
});
|
|
29
|
+
let unlinkAbort;
|
|
30
|
+
function stop() {
|
|
31
|
+
if (stopped) return;
|
|
32
|
+
stopped = true;
|
|
33
|
+
unlinkAbort?.();
|
|
34
|
+
unsubscribe();
|
|
35
|
+
}
|
|
36
|
+
unlinkAbort = linkAbortSignal(signal, stop);
|
|
37
|
+
return {
|
|
38
|
+
get dpr() {
|
|
39
|
+
return _dpr;
|
|
40
|
+
},
|
|
41
|
+
stop
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { PhaseError, clamp, clamp01, createDevicePixelRatio, createLifecycle, createLoop, createRenderState, createScrollProgress, createSight, createTicker, easeInOutCubic, easeOutBack, easeOutCubic, easeOutQuart, inverseLerp, isPhaseError, lerp, linear, prefersReducedMotion, remap, whenIdle };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/core/device-pixel-ratio/index.ts"],"sourcesContent":["import { linkAbortSignal } from '../_internal/abort';\nimport { serverContextError } from '../_internal/errors';\nimport { subscribeDpr, readDpr } from '../_internal/pool/dpr';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DevicePixelRatioOptions {\n /** Called when devicePixelRatio changes (e.g. window moved between monitors). */\n onChange: (dpr: number) => void;\n /** Abort signal that stops the watcher when aborted. */\n signal?: AbortSignal;\n}\n\nexport interface DevicePixelRatio {\n /** Current devicePixelRatio. Synchronous read of the last-reported value. */\n readonly dpr: number;\n stop(): void;\n}\n\n// ---------------------------------------------------------------------------\n// createDevicePixelRatio\n// ---------------------------------------------------------------------------\n\n/**\n * Track devicePixelRatio changes (e.g. user drags the window between monitors\n * with different pixel densities).\n *\n * Uses a shared `matchMedia` subscription that re-subscribes on every change,\n * so chained monitor switches (A -> B -> C) are all caught. Multiple instances\n * share one underlying subscription.\n *\n * @example\n * const watcher = createDevicePixelRatio({\n * onChange: (dpr) => bridge.setDpr(dpr),\n * });\n * watcher.dpr; // current value\n * // cleanup:\n * watcher.stop();\n */\nexport function createDevicePixelRatio(\n options: DevicePixelRatioOptions,\n): DevicePixelRatio {\n if (typeof matchMedia === 'undefined') {\n serverContextError('createDevicePixelRatio');\n }\n\n const { onChange, signal } = options;\n\n let _dpr: number = readDpr();\n let stopped = false;\n\n const unsubscribe: () => void = subscribeDpr((dpr) => {\n _dpr = dpr;\n onChange(dpr);\n });\n\n let unlinkAbort: (() => void) | undefined;\n\n function stop(): void {\n if (stopped) return;\n stopped = true;\n unlinkAbort?.();\n unsubscribe();\n }\n\n unlinkAbort = linkAbortSignal(signal, stop);\n\n return {\n get dpr(): number {\n return _dpr;\n },\n stop,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAyCA,SAAgB,uBACd,SACkB;AAClB,KAAI,OAAO,eAAe,YACxB,oBAAmB,yBAAyB;CAG9C,MAAM,EAAE,UAAU,WAAW;CAE7B,IAAI,OAAe,SAAS;CAC5B,IAAI,UAAU;CAEd,MAAM,cAA0B,cAAc,QAAQ;AACpD,SAAO;AACP,WAAS,IAAI;GACb;CAEF,IAAI;CAEJ,SAAS,OAAa;AACpB,MAAI,QAAS;AACb,YAAU;AACV,iBAAe;AACf,eAAa;;AAGf,eAAc,gBAAgB,QAAQ,KAAK;AAE3C,QAAO;EACL,IAAI,MAAc;AAChB,UAAO;;EAET;EACD"}
|