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/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
@@ -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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"}
@@ -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"}