phase 0.0.11 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -44
- package/dist/{debounce-BD9K4TW4.js → debounce-BX3NrBak.js} +68 -65
- package/dist/debounce-BX3NrBak.js.map +1 -0
- package/dist/{index-C10bmjm1.d.ts → index-DrG0DyLp.d.ts} +13 -13
- package/dist/{index-C10bmjm1.d.ts.map → index-DrG0DyLp.d.ts.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/react.d.ts +5 -5
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +26 -26
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/dist/debounce-BD9K4TW4.js.map +0 -1
package/README.md
CHANGED
|
@@ -176,9 +176,9 @@ The main primitive. Composes a ticker, visibility observer, and reduced-motion l
|
|
|
176
176
|
import { createLoop } from 'phase';
|
|
177
177
|
|
|
178
178
|
const loop = createLoop({
|
|
179
|
-
|
|
179
|
+
target: el,
|
|
180
180
|
onTick: (frame) => {
|
|
181
|
-
// frame.time —
|
|
181
|
+
// frame.time — browser rAF timestamp
|
|
182
182
|
// frame.delta — ms since last tick (clamped to 40ms)
|
|
183
183
|
// frame.elapsed — ms since start (paused time excluded)
|
|
184
184
|
// frame.frame — frame count
|
|
@@ -229,7 +229,7 @@ Controls the loop's response when quality degrades. Same three-value pattern as
|
|
|
229
229
|
|
|
230
230
|
```ts
|
|
231
231
|
createLoop({
|
|
232
|
-
|
|
232
|
+
target: el,
|
|
233
233
|
onTick: draw,
|
|
234
234
|
degraded: 'throttle', // default
|
|
235
235
|
degradedFps: 20, // only accepted when degraded is 'throttle'
|
|
@@ -240,7 +240,7 @@ createLoop({
|
|
|
240
240
|
|
|
241
241
|
| Option | Type | Default | Description |
|
|
242
242
|
| --------------- | ----------------------------------- | ------------ | ----------------------------------------- |
|
|
243
|
-
| `
|
|
243
|
+
| `target` | `Element` | required | Element to observe for visibility |
|
|
244
244
|
| `onTick` | `(frame: FrameState) => void` | required | Called each frame while running |
|
|
245
245
|
| `fps` | `number` | — | Cap frames per second |
|
|
246
246
|
| `reducedMotion` | `'pause' \| 'complete' \| 'ignore'` | `'pause'` | Behavior when user prefers reduced motion |
|
|
@@ -264,7 +264,7 @@ const ticker = createTicker({
|
|
|
264
264
|
ticker.start();
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
-
All tickers share a single `requestAnimationFrame` loop. Every subscriber
|
|
267
|
+
All tickers share a single `requestAnimationFrame` loop. Every subscriber receives the same browser-supplied timestamp each frame, so independent animations stay in visual sync.
|
|
268
268
|
|
|
269
269
|
#### Ticker phases
|
|
270
270
|
|
|
@@ -283,7 +283,7 @@ Answers one question: is this element visible right now? Combines `document.visi
|
|
|
283
283
|
import { createSight } from 'phase';
|
|
284
284
|
|
|
285
285
|
const sight = createSight({
|
|
286
|
-
|
|
286
|
+
target: el,
|
|
287
287
|
onPhaseChange: (phase, reason) => {
|
|
288
288
|
// phase: 'visible' | 'hidden' | 'unknown'
|
|
289
289
|
// reason: 'initial' | 'viewport' | 'document' | 'bfcache' | 'all-hidden'
|
|
@@ -303,7 +303,7 @@ Use `createLifecycle` when you own your render loop (a three.js/WebGL renderer,
|
|
|
303
303
|
import { createLifecycle } from 'phase';
|
|
304
304
|
|
|
305
305
|
const lifecycle = createLifecycle({
|
|
306
|
-
|
|
306
|
+
target: canvas,
|
|
307
307
|
onPhaseChange: (phase, reason) => {
|
|
308
308
|
// phase: 'idle' | 'active' | 'paused' | 'stopped'
|
|
309
309
|
// reason: 'started' | 'resumed' | 'sight' | 'reduced-motion' | 'manual' | 'disposed' | 'initial'
|
|
@@ -343,7 +343,7 @@ Reports what fraction of an element is currently visible in the viewport (0–1)
|
|
|
343
343
|
import { createScrollProgress } from 'phase';
|
|
344
344
|
|
|
345
345
|
const progress = createScrollProgress({
|
|
346
|
-
|
|
346
|
+
target: el,
|
|
347
347
|
onProgress: (ratio) => {
|
|
348
348
|
el.style.opacity = String(ratio);
|
|
349
349
|
},
|
|
@@ -361,7 +361,7 @@ The `steps` option controls threshold granularity. Default `20` generates 21 eve
|
|
|
361
361
|
|
|
362
362
|
| Option | Type | Default | Description |
|
|
363
363
|
| ------------ | ----------------------------- | -------- | ---------------------------------- |
|
|
364
|
-
| `
|
|
364
|
+
| `target` | `Element` | required | Element to observe |
|
|
365
365
|
| `onProgress` | `(ratio: number) => void` | required | Called at each threshold crossing |
|
|
366
366
|
| `steps` | `number` | `20` | Number of evenly-spaced thresholds |
|
|
367
367
|
| `root` | `Element \| Document \| null` | — | IO root element |
|
|
@@ -377,7 +377,7 @@ Tracks a scroll container's offset and progress. Reads `scrollLeft`/`scrollTop`
|
|
|
377
377
|
import { createScroll } from 'phase';
|
|
378
378
|
|
|
379
379
|
const scroll = createScroll({
|
|
380
|
-
|
|
380
|
+
target: viewport,
|
|
381
381
|
onScroll: (s) => {
|
|
382
382
|
// thumb CSS needs `transform-origin: left` so scaleX anchors to the track start
|
|
383
383
|
thumb.style.transform = `translateX(${s.progressX * (1 - s.visibleX) * 100}%) scaleX(${s.visibleX})`;
|
|
@@ -401,7 +401,7 @@ scroll.stop();
|
|
|
401
401
|
|
|
402
402
|
| Option | Type | Default | Description |
|
|
403
403
|
| --------------------- | ------------------------------ | --------- | -------------------------------------------------- |
|
|
404
|
-
| `
|
|
404
|
+
| `target` | `Element` | required | Scroll container to track |
|
|
405
405
|
| `onScroll` | `(state: ScrollState) => void` | required | Called once per rAF frame with position + progress |
|
|
406
406
|
| `onPhaseChange` | `(phase, reason) => void` | — | Called on phase transitions |
|
|
407
407
|
| `visibility` | `'pause' \| 'ignore'` | `'pause'` | Pause tracking when off-screen, or ignore |
|
|
@@ -481,7 +481,7 @@ Reports whether the browser is rendering an element or skipping it under `conten
|
|
|
481
481
|
import { createRenderState } from 'phase';
|
|
482
482
|
|
|
483
483
|
const renderState = createRenderState({
|
|
484
|
-
|
|
484
|
+
target: el,
|
|
485
485
|
onPhaseChange: (phase) => {
|
|
486
486
|
if (phase === 'skipped') clock.pause();
|
|
487
487
|
else clock.resume();
|
|
@@ -518,7 +518,7 @@ A lifecycle-aware `MutationObserver`: records are coalesced into one callback pe
|
|
|
518
518
|
import { createMutation } from 'phase';
|
|
519
519
|
|
|
520
520
|
const mutation = createMutation({
|
|
521
|
-
|
|
521
|
+
target: list,
|
|
522
522
|
mutation: { childList: true },
|
|
523
523
|
onMutations: (records) => syncItems(records),
|
|
524
524
|
});
|
|
@@ -536,7 +536,7 @@ Tracks pointer position relative to an element, batching high-frequency events i
|
|
|
536
536
|
import { createPointer } from 'phase';
|
|
537
537
|
|
|
538
538
|
const pointer = createPointer({
|
|
539
|
-
|
|
539
|
+
target: surface,
|
|
540
540
|
onPointer: (state) => {
|
|
541
541
|
cursor.style.transform = `translate(${state.x}px, ${state.y}px)`;
|
|
542
542
|
},
|
|
@@ -741,12 +741,12 @@ Animates a number from A to B over a duration. Calls `setState` per frame (appro
|
|
|
741
741
|
```tsx
|
|
742
742
|
import { useTween } from 'phase/react';
|
|
743
743
|
|
|
744
|
-
const opacity = useTween({
|
|
744
|
+
const opacity = useTween({ to: isVisible ? 1 : 0, duration: 300 });
|
|
745
745
|
```
|
|
746
746
|
|
|
747
747
|
Use `useTween` for single values where the render is cheap (counters, progress bars, opacity). Use `useLoop` when animating many elements or doing canvas work, since per-frame `setState` doesn't scale.
|
|
748
748
|
|
|
749
|
-
Reduced motion default: `'complete'` (jumps to
|
|
749
|
+
Reduced motion default: `'complete'` (jumps to the destination instantly). The value still reaches its destination; it skips the animation.
|
|
750
750
|
|
|
751
751
|
### usePresence
|
|
752
752
|
|
|
@@ -1195,7 +1195,7 @@ The rAF loop never triggers a React re-render. All per-frame state lives in refs
|
|
|
1195
1195
|
|
|
1196
1196
|
### Frame-locked shared clock
|
|
1197
1197
|
|
|
1198
|
-
All tickers share one `requestAnimationFrame` loop
|
|
1198
|
+
All tickers share one `requestAnimationFrame` loop and receive the same browser-supplied timestamp each frame, keeping multiple animations on the same page in visual sync.
|
|
1199
1199
|
|
|
1200
1200
|
### Delta clamping
|
|
1201
1201
|
|
|
@@ -1209,13 +1209,13 @@ Every error includes a machine-readable `code` and an actionable message.
|
|
|
1209
1209
|
import { PhaseError, isPhaseError } from 'phase';
|
|
1210
1210
|
```
|
|
1211
1211
|
|
|
1212
|
-
| Code | Trigger
|
|
1213
|
-
| ------------------ |
|
|
1214
|
-
| `server_context` | Calling a browser-only primitive during SSR
|
|
1215
|
-
| `
|
|
1216
|
-
| `invalid_duration` | `useTween` duration is zero, negative, or NaN
|
|
1217
|
-
| `ticker_stopped` | Calling `start`/`resume` on a stopped ticker
|
|
1218
|
-
| `missing_context` | `<Swap.State>` used outside `<Swap>`
|
|
1212
|
+
| Code | Trigger |
|
|
1213
|
+
| ------------------ | --------------------------------------------------- |
|
|
1214
|
+
| `server_context` | Calling a browser-only primitive during SSR |
|
|
1215
|
+
| `no_target` | Passing a null or undefined `target` to a primitive |
|
|
1216
|
+
| `invalid_duration` | `useTween` duration is zero, negative, or NaN |
|
|
1217
|
+
| `ticker_stopped` | Calling `start`/`resume` on a stopped ticker |
|
|
1218
|
+
| `missing_context` | `<Swap.State>` used outside `<Swap>` |
|
|
1219
1219
|
|
|
1220
1220
|
## Relationship to View Transitions
|
|
1221
1221
|
|
|
@@ -1232,35 +1232,35 @@ Minimal footprint is a core promise (see [Why phase](#why-phase)). Every export
|
|
|
1232
1232
|
| Export | Size (min+brotli) |
|
|
1233
1233
|
| ------------------------- | ----------------: |
|
|
1234
1234
|
| **Core** | |
|
|
1235
|
-
| `createTicker` |
|
|
1236
|
-
| `createSight` |
|
|
1235
|
+
| `createTicker` | 847 B |
|
|
1236
|
+
| `createSight` | 970 B |
|
|
1237
1237
|
| `createLifecycle` | 1.48 kB |
|
|
1238
|
-
| `createLoop` |
|
|
1239
|
-
| `createScrollProgress` |
|
|
1240
|
-
| `createRenderState` |
|
|
1238
|
+
| `createLoop` | 2.6 kB |
|
|
1239
|
+
| `createScrollProgress` | 865 B |
|
|
1240
|
+
| `createRenderState` | 490 B |
|
|
1241
1241
|
| `createDevicePixelRatio` | 544 B |
|
|
1242
|
-
| `createMutation` | 1.
|
|
1243
|
-
| `createPointer` | 1.
|
|
1244
|
-
| `createScroll` | 1.
|
|
1245
|
-
| `createThrottle` |
|
|
1246
|
-
| `createDebounce` |
|
|
1242
|
+
| `createMutation` | 1.17 kB |
|
|
1243
|
+
| `createPointer` | 1.26 kB |
|
|
1244
|
+
| `createScroll` | 1.46 kB |
|
|
1245
|
+
| `createThrottle` | 659 B |
|
|
1246
|
+
| `createDebounce` | 557 B |
|
|
1247
1247
|
| `whenIdle` | 409 B |
|
|
1248
1248
|
| `prefersReducedMotion` | 101 B |
|
|
1249
1249
|
| **Ease** | |
|
|
1250
1250
|
| `ease (all)` | 210 B |
|
|
1251
1251
|
| **React** | |
|
|
1252
|
-
| `useLoop` | 2.
|
|
1252
|
+
| `useLoop` | 2.85 kB |
|
|
1253
1253
|
| `useLifecycle` | 1.68 kB |
|
|
1254
1254
|
| `useSight` | 1.18 kB |
|
|
1255
|
-
| `useCanvas` | 3.
|
|
1256
|
-
| `useMutation` | 1.
|
|
1257
|
-
| `usePointer` | 1.
|
|
1255
|
+
| `useCanvas` | 3.47 kB |
|
|
1256
|
+
| `useMutation` | 1.37 kB |
|
|
1257
|
+
| `usePointer` | 1.48 kB |
|
|
1258
1258
|
| `useScroll` | 1.72 kB |
|
|
1259
1259
|
| `useThrottledCallback` | 797 B |
|
|
1260
|
-
| `useDebouncedCallback` |
|
|
1260
|
+
| `useDebouncedCallback` | 687 B |
|
|
1261
1261
|
| `useTween` | 655 B |
|
|
1262
1262
|
| `usePresence` | 591 B |
|
|
1263
|
-
| `useScrollProgress` |
|
|
1263
|
+
| `useScrollProgress` | 999 B |
|
|
1264
1264
|
| `useSize` | 378 B |
|
|
1265
1265
|
| `useContainerQuery` | 384 B |
|
|
1266
1266
|
| `useMediaQuery` | 246 B |
|
|
@@ -1269,13 +1269,13 @@ Minimal footprint is a core promise (see [Why phase](#why-phase)). Every export
|
|
|
1269
1269
|
| `useSyncedRef` | 22 B |
|
|
1270
1270
|
| `useStableCallback` | 39 B |
|
|
1271
1271
|
| `Presence` | 741 B |
|
|
1272
|
-
| `WhenVisible` | 1.
|
|
1272
|
+
| `WhenVisible` | 1.43 kB |
|
|
1273
1273
|
| `WhenIdle` | 593 B |
|
|
1274
1274
|
| `Defer` | 86 B |
|
|
1275
1275
|
| `useIdle` | 435 B |
|
|
1276
|
-
| `useWhenIdle` |
|
|
1277
|
-
| `useRenderState` |
|
|
1278
|
-
| `Swap` | 1.
|
|
1276
|
+
| `useWhenIdle` | 446 B |
|
|
1277
|
+
| `useRenderState` | 521 B |
|
|
1278
|
+
| `Swap` | 1.12 kB |
|
|
1279
1279
|
|
|
1280
1280
|
<!-- SIZE-TABLE:END -->
|
|
1281
1281
|
|
|
@@ -49,10 +49,10 @@ function serverContextError(fn) {
|
|
|
49
49
|
fix: "Move into a useEffect or client-only module."
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
-
function
|
|
53
|
-
throw new PhaseError(`${fn}() requires a
|
|
54
|
-
code: "
|
|
55
|
-
reason: "The
|
|
52
|
+
function noTargetError(fn) {
|
|
53
|
+
throw new PhaseError(`${fn}() requires a target.`, {
|
|
54
|
+
code: "no_target",
|
|
55
|
+
reason: "The target was null or undefined.",
|
|
56
56
|
fix: "Pass a mounted Element, or use the React hook which manages the ref."
|
|
57
57
|
});
|
|
58
58
|
}
|
|
@@ -83,28 +83,31 @@ function missingContextError(child, parent) {
|
|
|
83
83
|
const MAX_DELTA_MS = 40;
|
|
84
84
|
/** Default first-frame delta when no previous tick exists. */
|
|
85
85
|
const DEFAULT_FIRST_DELTA_MS = 16.67;
|
|
86
|
+
let sharedRafId = null;
|
|
87
|
+
let sharedFrame = 0;
|
|
86
88
|
let sharedTime = 0;
|
|
87
|
-
let sharedRafId = 0;
|
|
88
89
|
const sharedSubscribers = /* @__PURE__ */ new Set();
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
for (const callback of sharedSubscribers) callback();
|
|
92
|
-
if (sharedSubscribers.size > 0) sharedRafId = requestAnimationFrame(sharedTick);
|
|
90
|
+
function dispatchSharedSubscription(subscription) {
|
|
91
|
+
if (subscription.joinedFrame < sharedFrame) subscription.callback(sharedTime);
|
|
93
92
|
}
|
|
94
|
-
function
|
|
93
|
+
function sharedTick(time) {
|
|
94
|
+
sharedTime = time;
|
|
95
|
+
sharedFrame++;
|
|
96
|
+
sharedRafId = requestAnimationFrame(sharedTick);
|
|
97
|
+
sharedSubscribers.forEach(dispatchSharedSubscription);
|
|
98
|
+
}
|
|
99
|
+
function joinSharedClock(subscription) {
|
|
95
100
|
const wasEmpty = sharedSubscribers.size === 0;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
subscription.joinedFrame = sharedFrame;
|
|
102
|
+
sharedSubscribers.add(subscription);
|
|
103
|
+
if (wasEmpty) sharedRafId = requestAnimationFrame(sharedTick);
|
|
104
|
+
}
|
|
105
|
+
function leaveSharedClock(subscription) {
|
|
106
|
+
sharedSubscribers.delete(subscription);
|
|
107
|
+
if (sharedSubscribers.size === 0 && sharedRafId !== null) {
|
|
108
|
+
cancelAnimationFrame(sharedRafId);
|
|
109
|
+
sharedRafId = null;
|
|
100
110
|
}
|
|
101
|
-
return () => {
|
|
102
|
-
sharedSubscribers.delete(callback);
|
|
103
|
-
if (sharedSubscribers.size === 0 && sharedRafId) {
|
|
104
|
-
cancelAnimationFrame(sharedRafId);
|
|
105
|
-
sharedRafId = 0;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
111
|
}
|
|
109
112
|
function resetFrameState(state) {
|
|
110
113
|
state.time = 0;
|
|
@@ -125,7 +128,6 @@ function createTicker(options) {
|
|
|
125
128
|
const minFrameTime = fps ? 1e3 / fps : 0;
|
|
126
129
|
let _phase = "idle";
|
|
127
130
|
let _reason = "initial";
|
|
128
|
-
let leaveSharedClock = null;
|
|
129
131
|
let lastTickTime = 0;
|
|
130
132
|
let pauseStartTime = 0;
|
|
131
133
|
let totalPausedTime = 0;
|
|
@@ -136,8 +138,7 @@ function createTicker(options) {
|
|
|
136
138
|
elapsed: 0,
|
|
137
139
|
frame: 0
|
|
138
140
|
};
|
|
139
|
-
function tick() {
|
|
140
|
-
const now = sharedTime;
|
|
141
|
+
function tick(now) {
|
|
141
142
|
if (minFrameTime > 0 && now - lastTickTime < minFrameTime) return;
|
|
142
143
|
const rawDelta = lastTickTime === 0 ? DEFAULT_FIRST_DELTA_MS : now - lastTickTime;
|
|
143
144
|
lastTickTime = now;
|
|
@@ -147,6 +148,10 @@ function createTicker(options) {
|
|
|
147
148
|
frame.frame++;
|
|
148
149
|
onTick(frame);
|
|
149
150
|
}
|
|
151
|
+
const subscription = {
|
|
152
|
+
callback: tick,
|
|
153
|
+
joinedFrame: 0
|
|
154
|
+
};
|
|
150
155
|
function start() {
|
|
151
156
|
if (_phase === "running") return;
|
|
152
157
|
if (_phase === "stopped") tickerStoppedError();
|
|
@@ -160,15 +165,14 @@ function createTicker(options) {
|
|
|
160
165
|
lastTickTime = 0;
|
|
161
166
|
totalPausedTime = 0;
|
|
162
167
|
resetFrameState(frame);
|
|
163
|
-
|
|
168
|
+
joinSharedClock(subscription);
|
|
164
169
|
}
|
|
165
170
|
function pause() {
|
|
166
171
|
if (_phase !== "running") return;
|
|
167
172
|
_phase = "paused";
|
|
168
173
|
_reason = "manual";
|
|
169
174
|
pauseStartTime = performance.now();
|
|
170
|
-
leaveSharedClock
|
|
171
|
-
leaveSharedClock = null;
|
|
175
|
+
leaveSharedClock(subscription);
|
|
172
176
|
}
|
|
173
177
|
function resume() {
|
|
174
178
|
if (_phase === "stopped") tickerStoppedError();
|
|
@@ -177,15 +181,14 @@ function createTicker(options) {
|
|
|
177
181
|
lastTickTime = 0;
|
|
178
182
|
_phase = "running";
|
|
179
183
|
_reason = "resumed";
|
|
180
|
-
|
|
184
|
+
joinSharedClock(subscription);
|
|
181
185
|
}
|
|
182
186
|
function stop() {
|
|
183
187
|
if (_phase === "stopped") return;
|
|
184
188
|
_phase = "stopped";
|
|
185
189
|
_reason = _reason === "initial" ? "disposed" : "manual";
|
|
186
190
|
unlinkAbort?.();
|
|
187
|
-
leaveSharedClock
|
|
188
|
-
leaveSharedClock = null;
|
|
191
|
+
leaveSharedClock(subscription);
|
|
189
192
|
}
|
|
190
193
|
let unlinkAbort;
|
|
191
194
|
unlinkAbort = linkAbortSignal(signal, stop);
|
|
@@ -277,7 +280,7 @@ const createPoolEntry$1 = (options) => {
|
|
|
277
280
|
*
|
|
278
281
|
* @example
|
|
279
282
|
* const sight = createSight({
|
|
280
|
-
*
|
|
283
|
+
* target: el,
|
|
281
284
|
* onPhaseChange: (phase) => phase === 'visible' ? loop.start() : loop.pause(),
|
|
282
285
|
* });
|
|
283
286
|
* // cleanup:
|
|
@@ -288,8 +291,8 @@ const createPoolEntry$1 = (options) => {
|
|
|
288
291
|
*/
|
|
289
292
|
function createSight(options) {
|
|
290
293
|
if (typeof document === "undefined") serverContextError("createSight");
|
|
291
|
-
const {
|
|
292
|
-
if (!
|
|
294
|
+
const { target, intersectionOptions, onPhaseChange, signal } = options;
|
|
295
|
+
if (!target) noTargetError("createSight");
|
|
293
296
|
let _phase = "unknown";
|
|
294
297
|
let _reason = "initial";
|
|
295
298
|
let stopped = false;
|
|
@@ -320,7 +323,7 @@ function createSight(options) {
|
|
|
320
323
|
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
321
324
|
window.addEventListener("pageshow", onPageShow);
|
|
322
325
|
const unobserveIO = observeIntersection({
|
|
323
|
-
element,
|
|
326
|
+
element: target,
|
|
324
327
|
onIntersect: onIntersection,
|
|
325
328
|
...intersectionOptions
|
|
326
329
|
});
|
|
@@ -413,7 +416,7 @@ const REDUCED_MOTION_QUERY$1 = "(prefers-reduced-motion: reduce)";
|
|
|
413
416
|
*
|
|
414
417
|
* @example
|
|
415
418
|
* const lifecycle = createLifecycle({
|
|
416
|
-
*
|
|
419
|
+
* target: canvas,
|
|
417
420
|
* onPhaseChange: (phase) => {
|
|
418
421
|
* if (phase === 'active') renderer.start();
|
|
419
422
|
* else renderer.stop();
|
|
@@ -424,8 +427,8 @@ const REDUCED_MOTION_QUERY$1 = "(prefers-reduced-motion: reduce)";
|
|
|
424
427
|
*/
|
|
425
428
|
function createLifecycle(options) {
|
|
426
429
|
if (typeof document === "undefined") serverContextError("createLifecycle");
|
|
427
|
-
const {
|
|
428
|
-
if (!
|
|
430
|
+
const { target, reducedMotion = "pause", intersectionOptions, start: startMode = "auto", onPhaseChange, signal } = options;
|
|
431
|
+
if (!target) noTargetError("createLifecycle");
|
|
429
432
|
let _phase = "idle";
|
|
430
433
|
let _reason = "initial";
|
|
431
434
|
let sightVisible = false;
|
|
@@ -465,7 +468,7 @@ function createLifecycle(options) {
|
|
|
465
468
|
reconcile();
|
|
466
469
|
}
|
|
467
470
|
const sight = createSight({
|
|
468
|
-
|
|
471
|
+
target,
|
|
469
472
|
intersectionOptions,
|
|
470
473
|
onPhaseChange: onSightChange
|
|
471
474
|
});
|
|
@@ -542,7 +545,7 @@ const RECOVERY_RETRY_MS = 2e3;
|
|
|
542
545
|
*
|
|
543
546
|
* @example
|
|
544
547
|
* const loop = createLoop({
|
|
545
|
-
*
|
|
548
|
+
* target: el,
|
|
546
549
|
* onTick: (frame) => draw(ctx, frame),
|
|
547
550
|
* });
|
|
548
551
|
* // cleanup:
|
|
@@ -550,8 +553,8 @@ const RECOVERY_RETRY_MS = 2e3;
|
|
|
550
553
|
*/
|
|
551
554
|
function createLoop(options) {
|
|
552
555
|
if (typeof requestAnimationFrame === "undefined") serverContextError("createLoop");
|
|
553
|
-
const {
|
|
554
|
-
if (!
|
|
556
|
+
const { target, onTick, fps: baseFps, reducedMotion = "pause", degraded = "throttle", degradedFps: configuredDegradedFps, intersectionOptions, start: startMode = "auto", onPhaseChange, signal } = options;
|
|
557
|
+
if (!target) noTargetError("createLoop");
|
|
555
558
|
const degradedFps = degraded === "throttle" ? configuredDegradedFps : void 0;
|
|
556
559
|
let _phase = "idle";
|
|
557
560
|
let _reason = "initial";
|
|
@@ -672,7 +675,7 @@ function createLoop(options) {
|
|
|
672
675
|
reconcileQuality();
|
|
673
676
|
}
|
|
674
677
|
const lifecycle = createLifecycle({
|
|
675
|
-
|
|
678
|
+
target,
|
|
676
679
|
reducedMotion: reducedMotion === "pause" ? "pause" : "ignore",
|
|
677
680
|
intersectionOptions,
|
|
678
681
|
start: "manual",
|
|
@@ -742,7 +745,7 @@ function buildThresholds(steps) {
|
|
|
742
745
|
*
|
|
743
746
|
* @example
|
|
744
747
|
* const progress = createScrollProgress({
|
|
745
|
-
*
|
|
748
|
+
* target: el,
|
|
746
749
|
* onProgress: (ratio) => {
|
|
747
750
|
* el.style.opacity = String(ratio);
|
|
748
751
|
* },
|
|
@@ -752,8 +755,8 @@ function buildThresholds(steps) {
|
|
|
752
755
|
*/
|
|
753
756
|
function createScrollProgress(options) {
|
|
754
757
|
if (typeof IntersectionObserver === "undefined") serverContextError("createScrollProgress");
|
|
755
|
-
const { element, onProgress, steps = DEFAULT_STEPS, root, rootMargin, signal } = options;
|
|
756
|
-
if (!element)
|
|
758
|
+
const { target: element, onProgress, steps = DEFAULT_STEPS, root, rootMargin, signal } = options;
|
|
759
|
+
if (!element) noTargetError("createScrollProgress");
|
|
757
760
|
let _ratio = 0;
|
|
758
761
|
let stopped = false;
|
|
759
762
|
const unobserve = observeIntersection({
|
|
@@ -799,7 +802,7 @@ function createScrollProgress(options) {
|
|
|
799
802
|
*
|
|
800
803
|
* @example
|
|
801
804
|
* const render = createRenderState({
|
|
802
|
-
*
|
|
805
|
+
* target: el,
|
|
803
806
|
* onPhaseChange: (phase) => phase === 'skipped' ? clock.pause() : clock.resume(),
|
|
804
807
|
* });
|
|
805
808
|
* // cleanup:
|
|
@@ -814,8 +817,8 @@ function createScrollProgress(options) {
|
|
|
814
817
|
*/
|
|
815
818
|
function createRenderState(options) {
|
|
816
819
|
if (typeof document === "undefined") serverContextError("createRenderState");
|
|
817
|
-
const { element, onPhaseChange, signal } = options;
|
|
818
|
-
if (!element)
|
|
820
|
+
const { target: element, onPhaseChange, signal } = options;
|
|
821
|
+
if (!element) noTargetError("createRenderState");
|
|
819
822
|
let _phase = "rendered";
|
|
820
823
|
let stopped = false;
|
|
821
824
|
function onStateChange(event) {
|
|
@@ -944,8 +947,8 @@ function prefersReducedMotion() {
|
|
|
944
947
|
/** Lifecycle-aware MutationObserver with rAF-coalesced callbacks. */
|
|
945
948
|
function createMutation(options) {
|
|
946
949
|
if (typeof document === "undefined") serverContextError("createMutation");
|
|
947
|
-
const { element, mutation: mutationInit, onMutations, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
948
|
-
if (!element)
|
|
950
|
+
const { target: element, mutation: mutationInit, onMutations, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
951
|
+
if (!element) noTargetError("createMutation");
|
|
949
952
|
warnReflowStorm(mutationInit);
|
|
950
953
|
let _phase = "paused";
|
|
951
954
|
let _reason = "initial";
|
|
@@ -1062,8 +1065,8 @@ function warnReflowStorm(opts) {
|
|
|
1062
1065
|
/** Lifecycle-aware pointer tracker with rAF-batched `getBoundingClientRect`. */
|
|
1063
1066
|
function createPointer(options) {
|
|
1064
1067
|
if (typeof document === "undefined") serverContextError("createPointer");
|
|
1065
|
-
const { element, onPointer, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
1066
|
-
if (!element)
|
|
1068
|
+
const { target: element, onPointer, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
1069
|
+
if (!element) noTargetError("createPointer");
|
|
1067
1070
|
let _phase = "idle";
|
|
1068
1071
|
let _reason = "initial";
|
|
1069
1072
|
let stopped = false;
|
|
@@ -1251,8 +1254,8 @@ function getObserver() {
|
|
|
1251
1254
|
*/
|
|
1252
1255
|
function createScroll(options) {
|
|
1253
1256
|
if (typeof document === "undefined") serverContextError("createScroll");
|
|
1254
|
-
const {
|
|
1255
|
-
if (!
|
|
1257
|
+
const { target, onScroll, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
1258
|
+
if (!target) noTargetError("createScroll");
|
|
1256
1259
|
let _phase = "paused";
|
|
1257
1260
|
let _reason = "initial";
|
|
1258
1261
|
let stopped = false;
|
|
@@ -1278,8 +1281,8 @@ function createScroll(options) {
|
|
|
1278
1281
|
}
|
|
1279
1282
|
function computePosition() {
|
|
1280
1283
|
const { maxX, maxY } = _state;
|
|
1281
|
-
const x =
|
|
1282
|
-
const y =
|
|
1284
|
+
const x = target.scrollLeft;
|
|
1285
|
+
const y = target.scrollTop;
|
|
1283
1286
|
_state.x = x < 0 ? 0 : x > maxX ? maxX : x;
|
|
1284
1287
|
_state.y = y < 0 ? 0 : y > maxY ? maxY : y;
|
|
1285
1288
|
_state.progressX = maxX > 0 ? _state.x / maxX : 0;
|
|
@@ -1287,10 +1290,10 @@ function createScroll(options) {
|
|
|
1287
1290
|
}
|
|
1288
1291
|
function measure() {
|
|
1289
1292
|
if (stopped || !listenersAttached) return;
|
|
1290
|
-
const scrollWidth =
|
|
1291
|
-
const clientWidth =
|
|
1292
|
-
const scrollHeight =
|
|
1293
|
-
const clientHeight =
|
|
1293
|
+
const scrollWidth = target.scrollWidth;
|
|
1294
|
+
const clientWidth = target.clientWidth;
|
|
1295
|
+
const scrollHeight = target.scrollHeight;
|
|
1296
|
+
const clientHeight = target.clientHeight;
|
|
1294
1297
|
const maxX = scrollWidth - clientWidth;
|
|
1295
1298
|
const maxY = scrollHeight - clientHeight;
|
|
1296
1299
|
_state.maxX = maxX > 0 ? maxX : 0;
|
|
@@ -1330,14 +1333,14 @@ function createScroll(options) {
|
|
|
1330
1333
|
function attachListeners() {
|
|
1331
1334
|
if (listenersAttached) return;
|
|
1332
1335
|
listenersAttached = true;
|
|
1333
|
-
|
|
1334
|
-
unobserveRO = observeResize(
|
|
1336
|
+
target.addEventListener("scroll", onScrollEvent, { passive: true });
|
|
1337
|
+
unobserveRO = observeResize(target, onROResize);
|
|
1335
1338
|
measure();
|
|
1336
1339
|
}
|
|
1337
1340
|
function detachListeners() {
|
|
1338
1341
|
if (!listenersAttached) return;
|
|
1339
1342
|
listenersAttached = false;
|
|
1340
|
-
|
|
1343
|
+
target.removeEventListener("scroll", onScrollEvent);
|
|
1341
1344
|
unobserveRO?.();
|
|
1342
1345
|
unobserveRO = void 0;
|
|
1343
1346
|
cancelFlush();
|
|
@@ -1369,7 +1372,7 @@ function createScroll(options) {
|
|
|
1369
1372
|
document.addEventListener("visibilitychange", onVisChange);
|
|
1370
1373
|
window.addEventListener("pageshow", onPageShow);
|
|
1371
1374
|
const unobserveIO = observeIntersection({
|
|
1372
|
-
element,
|
|
1375
|
+
element: target,
|
|
1373
1376
|
onIntersect: (entry) => {
|
|
1374
1377
|
elementInView = entry.isIntersecting;
|
|
1375
1378
|
recompute();
|
|
@@ -1615,4 +1618,4 @@ function createDebounce(options) {
|
|
|
1615
1618
|
//#endregion
|
|
1616
1619
|
export { missingContextError as C, isPhaseError as S, linkAbortSignal as T, subscribeMediaQuery as _, createPointer as a, PhaseError as b, prefersReducedMotion as c, subscribeDpr as d, createRenderState as f, readMediaQuery as g, createLifecycle as h, observeResize as i, whenIdle as l, createLoop as m, createThrottle as n, createMutation as o, createScrollProgress as p, createScroll as r, REDUCED_MOTION_QUERY as s, createDebounce as t, readDpr as u, createSight as v, serverContextError as w, invalidDurationError as x, createTicker as y };
|
|
1617
1620
|
|
|
1618
|
-
//# sourceMappingURL=debounce-
|
|
1621
|
+
//# sourceMappingURL=debounce-BX3NrBak.js.map
|