phase 0.0.12 → 0.2.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 -41
- package/dist/{debounce-C0SgiTfm.js → debounce-V_5xqZSY.js} +83 -44
- package/dist/debounce-V_5xqZSY.js.map +1 -0
- package/dist/{index-BoQBL4QY.d.ts → index-MqCTTujG.d.ts} +22 -13
- package/dist/{index-BoQBL4QY.d.ts.map → index-MqCTTujG.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 +15 -6
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +35 -30
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/dist/debounce-C0SgiTfm.js.map +0 -1
package/README.md
CHANGED
|
@@ -176,7 +176,7 @@ 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
181
|
// frame.time — browser rAF timestamp
|
|
182
182
|
// frame.delta — ms since last tick (clamped to 40ms)
|
|
@@ -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 |
|
|
@@ -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'
|
|
@@ -337,13 +337,13 @@ Pause priority is `reduced-motion` > `sight` > `manual`.
|
|
|
337
337
|
|
|
338
338
|
Reports what fraction of an element is currently visible in the viewport (0–1), via the shared IntersectionObserver pool. Zero forced reflows, zero extra observers. Ideal for reveal/opacity effects.
|
|
339
339
|
|
|
340
|
-
> **Visibility ratio, not scroll offset.** This reports `intersectionRatio` (how much of an element is visible in the viewport), which plateaus for tall elements once they fill it. For a scroll container's _own_ offset (scrollbars, carousels) use [`createScroll`](#createscroll); for CSS-declarative scroll-linked animation use the native `ScrollTimeline` API; for spring/gesture scroll use `motion`.
|
|
340
|
+
> **Visibility ratio, not scroll offset.** This reports `intersectionRatio` (how much of an element is visible in the viewport), which plateaus for tall elements once they fill it. For a scroll container's _own_ offset (scrollbars, carousels, or the page via `target: 'page'` on the hook) use [`createScroll`](#createscroll); for CSS-declarative scroll-linked animation use the native `ScrollTimeline` API; for spring/gesture scroll use `motion`.
|
|
341
341
|
|
|
342
342
|
```ts
|
|
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 \| Document` | required | Scroll container, or `document` for the page |
|
|
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 |
|
|
@@ -410,6 +410,8 @@ scroll.stop();
|
|
|
410
410
|
|
|
411
411
|
The options type is `CreateScrollOptions` (`ScrollOptions` is a `lib.dom` global and must not be shadowed).
|
|
412
412
|
|
|
413
|
+
Pass `document` to track the page scroller. Offsets and geometry then come from `document.scrollingElement`, and since the page is never off-screen, `visibility: 'pause'` reacts to tab visibility alone and creates no `IntersectionObserver`. Use it for scroll progress bars, condensing headers, and scroll-to-top affordances instead of a bare `window` scroll listener.
|
|
414
|
+
|
|
413
415
|
### createThrottle
|
|
414
416
|
|
|
415
417
|
Frame-aligned, visibility-aware throttle for event-driven work below frame rate (socket emits, worker messaging, expensive recompute). Leading calls fire synchronously; a pending trailing call fires with the latest value on the first animation frame at or past `interval`. Nothing is scheduled while the trigger is idle or the document is hidden.
|
|
@@ -481,7 +483,7 @@ Reports whether the browser is rendering an element or skipping it under `conten
|
|
|
481
483
|
import { createRenderState } from 'phase';
|
|
482
484
|
|
|
483
485
|
const renderState = createRenderState({
|
|
484
|
-
|
|
486
|
+
target: el,
|
|
485
487
|
onPhaseChange: (phase) => {
|
|
486
488
|
if (phase === 'skipped') clock.pause();
|
|
487
489
|
else clock.resume();
|
|
@@ -518,7 +520,7 @@ A lifecycle-aware `MutationObserver`: records are coalesced into one callback pe
|
|
|
518
520
|
import { createMutation } from 'phase';
|
|
519
521
|
|
|
520
522
|
const mutation = createMutation({
|
|
521
|
-
|
|
523
|
+
target: list,
|
|
522
524
|
mutation: { childList: true },
|
|
523
525
|
onMutations: (records) => syncItems(records),
|
|
524
526
|
});
|
|
@@ -536,7 +538,7 @@ Tracks pointer position relative to an element, batching high-frequency events i
|
|
|
536
538
|
import { createPointer } from 'phase';
|
|
537
539
|
|
|
538
540
|
const pointer = createPointer({
|
|
539
|
-
|
|
541
|
+
target: surface,
|
|
540
542
|
onPointer: (state) => {
|
|
541
543
|
cursor.style.transform = `translate(${state.x}px, ${state.y}px)`;
|
|
542
544
|
},
|
|
@@ -741,12 +743,12 @@ Animates a number from A to B over a duration. Calls `setState` per frame (appro
|
|
|
741
743
|
```tsx
|
|
742
744
|
import { useTween } from 'phase/react';
|
|
743
745
|
|
|
744
|
-
const opacity = useTween({
|
|
746
|
+
const opacity = useTween({ to: isVisible ? 1 : 0, duration: 300 });
|
|
745
747
|
```
|
|
746
748
|
|
|
747
749
|
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
750
|
|
|
749
|
-
Reduced motion default: `'complete'` (jumps to
|
|
751
|
+
Reduced motion default: `'complete'` (jumps to the destination instantly). The value still reaches its destination; it skips the animation.
|
|
750
752
|
|
|
751
753
|
### usePresence
|
|
752
754
|
|
|
@@ -1209,13 +1211,14 @@ Every error includes a machine-readable `code` and an actionable message.
|
|
|
1209
1211
|
import { PhaseError, isPhaseError } from 'phase';
|
|
1210
1212
|
```
|
|
1211
1213
|
|
|
1212
|
-
| Code
|
|
1213
|
-
|
|
|
1214
|
-
| `server_context`
|
|
1215
|
-
| `
|
|
1216
|
-
| `
|
|
1217
|
-
| `
|
|
1218
|
-
| `
|
|
1214
|
+
| Code | Trigger |
|
|
1215
|
+
| -------------------- | --------------------------------------------------- |
|
|
1216
|
+
| `server_context` | Calling a browser-only primitive during SSR |
|
|
1217
|
+
| `no_target` | Passing a null or undefined `target` to a primitive |
|
|
1218
|
+
| `conflicting_target` | Passing both `ref` and `target` to a hook |
|
|
1219
|
+
| `invalid_duration` | `useTween` duration is zero, negative, or NaN |
|
|
1220
|
+
| `ticker_stopped` | Calling `start`/`resume` on a stopped ticker |
|
|
1221
|
+
| `missing_context` | `<Swap.State>` used outside `<Swap>` |
|
|
1219
1222
|
|
|
1220
1223
|
## Relationship to View Transitions
|
|
1221
1224
|
|
|
@@ -1232,18 +1235,18 @@ Minimal footprint is a core promise (see [Why phase](#why-phase)). Every export
|
|
|
1232
1235
|
| Export | Size (min+brotli) |
|
|
1233
1236
|
| ------------------------- | ----------------: |
|
|
1234
1237
|
| **Core** | |
|
|
1235
|
-
| `createTicker` |
|
|
1236
|
-
| `createSight` |
|
|
1238
|
+
| `createTicker` | 847 B |
|
|
1239
|
+
| `createSight` | 970 B |
|
|
1237
1240
|
| `createLifecycle` | 1.48 kB |
|
|
1238
|
-
| `createLoop` |
|
|
1239
|
-
| `createScrollProgress` |
|
|
1240
|
-
| `createRenderState` |
|
|
1241
|
+
| `createLoop` | 2.6 kB |
|
|
1242
|
+
| `createScrollProgress` | 869 B |
|
|
1243
|
+
| `createRenderState` | 490 B |
|
|
1241
1244
|
| `createDevicePixelRatio` | 544 B |
|
|
1242
|
-
| `createMutation` | 1.
|
|
1243
|
-
| `createPointer` | 1.
|
|
1244
|
-
| `createScroll` | 1.
|
|
1245
|
-
| `createThrottle` |
|
|
1246
|
-
| `createDebounce` |
|
|
1245
|
+
| `createMutation` | 1.17 kB |
|
|
1246
|
+
| `createPointer` | 1.26 kB |
|
|
1247
|
+
| `createScroll` | 1.56 kB |
|
|
1248
|
+
| `createThrottle` | 660 B |
|
|
1249
|
+
| `createDebounce` | 558 B |
|
|
1247
1250
|
| `whenIdle` | 409 B |
|
|
1248
1251
|
| `prefersReducedMotion` | 101 B |
|
|
1249
1252
|
| **Ease** | |
|
|
@@ -1255,12 +1258,12 @@ Minimal footprint is a core promise (see [Why phase](#why-phase)). Every export
|
|
|
1255
1258
|
| `useCanvas` | 3.47 kB |
|
|
1256
1259
|
| `useMutation` | 1.36 kB |
|
|
1257
1260
|
| `usePointer` | 1.47 kB |
|
|
1258
|
-
| `useScroll` |
|
|
1261
|
+
| `useScroll` | 1.9 kB |
|
|
1259
1262
|
| `useThrottledCallback` | 797 B |
|
|
1260
|
-
| `useDebouncedCallback` |
|
|
1261
|
-
| `useTween` |
|
|
1263
|
+
| `useDebouncedCallback` | 690 B |
|
|
1264
|
+
| `useTween` | 680 B |
|
|
1262
1265
|
| `usePresence` | 591 B |
|
|
1263
|
-
| `useScrollProgress` |
|
|
1266
|
+
| `useScrollProgress` | 1 kB |
|
|
1264
1267
|
| `useSize` | 378 B |
|
|
1265
1268
|
| `useContainerQuery` | 384 B |
|
|
1266
1269
|
| `useMediaQuery` | 246 B |
|
|
@@ -1269,13 +1272,13 @@ Minimal footprint is a core promise (see [Why phase](#why-phase)). Every export
|
|
|
1269
1272
|
| `useSyncedRef` | 22 B |
|
|
1270
1273
|
| `useStableCallback` | 39 B |
|
|
1271
1274
|
| `Presence` | 741 B |
|
|
1272
|
-
| `WhenVisible` | 1.
|
|
1273
|
-
| `WhenIdle` |
|
|
1275
|
+
| `WhenVisible` | 1.43 kB |
|
|
1276
|
+
| `WhenIdle` | 592 B |
|
|
1274
1277
|
| `Defer` | 86 B |
|
|
1275
1278
|
| `useIdle` | 435 B |
|
|
1276
|
-
| `useWhenIdle` |
|
|
1277
|
-
| `useRenderState` |
|
|
1278
|
-
| `Swap` | 1.
|
|
1279
|
+
| `useWhenIdle` | 443 B |
|
|
1280
|
+
| `useRenderState` | 521 B |
|
|
1281
|
+
| `Swap` | 1.12 kB |
|
|
1279
1282
|
|
|
1280
1283
|
<!-- SIZE-TABLE:END -->
|
|
1281
1284
|
|
|
@@ -49,13 +49,20 @@ 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
|
}
|
|
59
|
+
function conflictingTargetError(fn) {
|
|
60
|
+
throw new PhaseError(`${fn}() received both ref and target.`, {
|
|
61
|
+
code: "conflicting_target",
|
|
62
|
+
reason: "A tracker has one anchor.",
|
|
63
|
+
fix: "Pass ref for an element, or target: 'page' for the page."
|
|
64
|
+
});
|
|
65
|
+
}
|
|
59
66
|
function invalidDurationError(fn, value) {
|
|
60
67
|
throw new PhaseError(`${fn}() received an invalid duration: ${value}`, {
|
|
61
68
|
code: "invalid_duration",
|
|
@@ -280,7 +287,7 @@ const createPoolEntry$1 = (options) => {
|
|
|
280
287
|
*
|
|
281
288
|
* @example
|
|
282
289
|
* const sight = createSight({
|
|
283
|
-
*
|
|
290
|
+
* target: el,
|
|
284
291
|
* onPhaseChange: (phase) => phase === 'visible' ? loop.start() : loop.pause(),
|
|
285
292
|
* });
|
|
286
293
|
* // cleanup:
|
|
@@ -291,8 +298,8 @@ const createPoolEntry$1 = (options) => {
|
|
|
291
298
|
*/
|
|
292
299
|
function createSight(options) {
|
|
293
300
|
if (typeof document === "undefined") serverContextError("createSight");
|
|
294
|
-
const {
|
|
295
|
-
if (!
|
|
301
|
+
const { target, intersectionOptions, onPhaseChange, signal } = options;
|
|
302
|
+
if (!target) noTargetError("createSight");
|
|
296
303
|
let _phase = "unknown";
|
|
297
304
|
let _reason = "initial";
|
|
298
305
|
let stopped = false;
|
|
@@ -323,7 +330,7 @@ function createSight(options) {
|
|
|
323
330
|
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
324
331
|
window.addEventListener("pageshow", onPageShow);
|
|
325
332
|
const unobserveIO = observeIntersection({
|
|
326
|
-
element,
|
|
333
|
+
element: target,
|
|
327
334
|
onIntersect: onIntersection,
|
|
328
335
|
...intersectionOptions
|
|
329
336
|
});
|
|
@@ -416,7 +423,7 @@ const REDUCED_MOTION_QUERY$1 = "(prefers-reduced-motion: reduce)";
|
|
|
416
423
|
*
|
|
417
424
|
* @example
|
|
418
425
|
* const lifecycle = createLifecycle({
|
|
419
|
-
*
|
|
426
|
+
* target: canvas,
|
|
420
427
|
* onPhaseChange: (phase) => {
|
|
421
428
|
* if (phase === 'active') renderer.start();
|
|
422
429
|
* else renderer.stop();
|
|
@@ -427,8 +434,8 @@ const REDUCED_MOTION_QUERY$1 = "(prefers-reduced-motion: reduce)";
|
|
|
427
434
|
*/
|
|
428
435
|
function createLifecycle(options) {
|
|
429
436
|
if (typeof document === "undefined") serverContextError("createLifecycle");
|
|
430
|
-
const {
|
|
431
|
-
if (!
|
|
437
|
+
const { target, reducedMotion = "pause", intersectionOptions, start: startMode = "auto", onPhaseChange, signal } = options;
|
|
438
|
+
if (!target) noTargetError("createLifecycle");
|
|
432
439
|
let _phase = "idle";
|
|
433
440
|
let _reason = "initial";
|
|
434
441
|
let sightVisible = false;
|
|
@@ -468,7 +475,7 @@ function createLifecycle(options) {
|
|
|
468
475
|
reconcile();
|
|
469
476
|
}
|
|
470
477
|
const sight = createSight({
|
|
471
|
-
|
|
478
|
+
target,
|
|
472
479
|
intersectionOptions,
|
|
473
480
|
onPhaseChange: onSightChange
|
|
474
481
|
});
|
|
@@ -545,7 +552,7 @@ const RECOVERY_RETRY_MS = 2e3;
|
|
|
545
552
|
*
|
|
546
553
|
* @example
|
|
547
554
|
* const loop = createLoop({
|
|
548
|
-
*
|
|
555
|
+
* target: el,
|
|
549
556
|
* onTick: (frame) => draw(ctx, frame),
|
|
550
557
|
* });
|
|
551
558
|
* // cleanup:
|
|
@@ -553,8 +560,8 @@ const RECOVERY_RETRY_MS = 2e3;
|
|
|
553
560
|
*/
|
|
554
561
|
function createLoop(options) {
|
|
555
562
|
if (typeof requestAnimationFrame === "undefined") serverContextError("createLoop");
|
|
556
|
-
const {
|
|
557
|
-
if (!
|
|
563
|
+
const { target, onTick, fps: baseFps, reducedMotion = "pause", degraded = "throttle", degradedFps: configuredDegradedFps, intersectionOptions, start: startMode = "auto", onPhaseChange, signal } = options;
|
|
564
|
+
if (!target) noTargetError("createLoop");
|
|
558
565
|
const degradedFps = degraded === "throttle" ? configuredDegradedFps : void 0;
|
|
559
566
|
let _phase = "idle";
|
|
560
567
|
let _reason = "initial";
|
|
@@ -675,7 +682,7 @@ function createLoop(options) {
|
|
|
675
682
|
reconcileQuality();
|
|
676
683
|
}
|
|
677
684
|
const lifecycle = createLifecycle({
|
|
678
|
-
|
|
685
|
+
target,
|
|
679
686
|
reducedMotion: reducedMotion === "pause" ? "pause" : "ignore",
|
|
680
687
|
intersectionOptions,
|
|
681
688
|
start: "manual",
|
|
@@ -745,7 +752,7 @@ function buildThresholds(steps) {
|
|
|
745
752
|
*
|
|
746
753
|
* @example
|
|
747
754
|
* const progress = createScrollProgress({
|
|
748
|
-
*
|
|
755
|
+
* target: el,
|
|
749
756
|
* onProgress: (ratio) => {
|
|
750
757
|
* el.style.opacity = String(ratio);
|
|
751
758
|
* },
|
|
@@ -755,8 +762,8 @@ function buildThresholds(steps) {
|
|
|
755
762
|
*/
|
|
756
763
|
function createScrollProgress(options) {
|
|
757
764
|
if (typeof IntersectionObserver === "undefined") serverContextError("createScrollProgress");
|
|
758
|
-
const { element, onProgress, steps = DEFAULT_STEPS, root, rootMargin, signal } = options;
|
|
759
|
-
if (!element)
|
|
765
|
+
const { target: element, onProgress, steps = DEFAULT_STEPS, root, rootMargin, signal } = options;
|
|
766
|
+
if (!element) noTargetError("createScrollProgress");
|
|
760
767
|
let _ratio = 0;
|
|
761
768
|
let stopped = false;
|
|
762
769
|
const unobserve = observeIntersection({
|
|
@@ -802,7 +809,7 @@ function createScrollProgress(options) {
|
|
|
802
809
|
*
|
|
803
810
|
* @example
|
|
804
811
|
* const render = createRenderState({
|
|
805
|
-
*
|
|
812
|
+
* target: el,
|
|
806
813
|
* onPhaseChange: (phase) => phase === 'skipped' ? clock.pause() : clock.resume(),
|
|
807
814
|
* });
|
|
808
815
|
* // cleanup:
|
|
@@ -817,8 +824,8 @@ function createScrollProgress(options) {
|
|
|
817
824
|
*/
|
|
818
825
|
function createRenderState(options) {
|
|
819
826
|
if (typeof document === "undefined") serverContextError("createRenderState");
|
|
820
|
-
const { element, onPhaseChange, signal } = options;
|
|
821
|
-
if (!element)
|
|
827
|
+
const { target: element, onPhaseChange, signal } = options;
|
|
828
|
+
if (!element) noTargetError("createRenderState");
|
|
822
829
|
let _phase = "rendered";
|
|
823
830
|
let stopped = false;
|
|
824
831
|
function onStateChange(event) {
|
|
@@ -947,8 +954,8 @@ function prefersReducedMotion() {
|
|
|
947
954
|
/** Lifecycle-aware MutationObserver with rAF-coalesced callbacks. */
|
|
948
955
|
function createMutation(options) {
|
|
949
956
|
if (typeof document === "undefined") serverContextError("createMutation");
|
|
950
|
-
const { element, mutation: mutationInit, onMutations, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
951
|
-
if (!element)
|
|
957
|
+
const { target: element, mutation: mutationInit, onMutations, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
958
|
+
if (!element) noTargetError("createMutation");
|
|
952
959
|
warnReflowStorm(mutationInit);
|
|
953
960
|
let _phase = "paused";
|
|
954
961
|
let _reason = "initial";
|
|
@@ -1065,8 +1072,8 @@ function warnReflowStorm(opts) {
|
|
|
1065
1072
|
/** Lifecycle-aware pointer tracker with rAF-batched `getBoundingClientRect`. */
|
|
1066
1073
|
function createPointer(options) {
|
|
1067
1074
|
if (typeof document === "undefined") serverContextError("createPointer");
|
|
1068
|
-
const { element, onPointer, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
1069
|
-
if (!element)
|
|
1075
|
+
const { target: element, onPointer, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
1076
|
+
if (!element) noTargetError("createPointer");
|
|
1070
1077
|
let _phase = "idle";
|
|
1071
1078
|
let _reason = "initial";
|
|
1072
1079
|
let stopped = false;
|
|
@@ -1251,11 +1258,22 @@ function getObserver() {
|
|
|
1251
1258
|
* This is to `scroll` + `scrollWidth` what `createPointer` is to `pointermove`
|
|
1252
1259
|
* + `getBoundingClientRect`: the layout read is batched off the hot path so the
|
|
1253
1260
|
* per-scroll work is a single cheap position read plus math on cached geometry.
|
|
1261
|
+
*
|
|
1262
|
+
* Pass `document` to track the page. Offsets and geometry then come from
|
|
1263
|
+
* `document.scrollingElement`, and because the page is always in view,
|
|
1264
|
+
* `visibility: 'pause'` reacts to tab visibility alone rather than an
|
|
1265
|
+
* `IntersectionObserver`.
|
|
1254
1266
|
*/
|
|
1255
1267
|
function createScroll(options) {
|
|
1256
1268
|
if (typeof document === "undefined") serverContextError("createScroll");
|
|
1257
|
-
const {
|
|
1258
|
-
if (!
|
|
1269
|
+
const { target, onScroll, onPhaseChange, visibility = "pause", intersectionOptions, signal } = options;
|
|
1270
|
+
if (!target) noTargetError("createScroll");
|
|
1271
|
+
let pageDoc;
|
|
1272
|
+
let scroller;
|
|
1273
|
+
if (isDocument(target)) {
|
|
1274
|
+
pageDoc = target;
|
|
1275
|
+
scroller = target.scrollingElement ?? target.documentElement;
|
|
1276
|
+
} else scroller = target;
|
|
1259
1277
|
let _phase = "paused";
|
|
1260
1278
|
let _reason = "initial";
|
|
1261
1279
|
let stopped = false;
|
|
@@ -1271,6 +1289,7 @@ function createScroll(options) {
|
|
|
1271
1289
|
};
|
|
1272
1290
|
let rafId = 0;
|
|
1273
1291
|
let dirty = false;
|
|
1292
|
+
let needsMeasure = false;
|
|
1274
1293
|
let listenersAttached = false;
|
|
1275
1294
|
let unobserveRO;
|
|
1276
1295
|
function setPhase(phase, reason) {
|
|
@@ -1281,8 +1300,8 @@ function createScroll(options) {
|
|
|
1281
1300
|
}
|
|
1282
1301
|
function computePosition() {
|
|
1283
1302
|
const { maxX, maxY } = _state;
|
|
1284
|
-
const x =
|
|
1285
|
-
const y =
|
|
1303
|
+
const x = scroller.scrollLeft;
|
|
1304
|
+
const y = scroller.scrollTop;
|
|
1286
1305
|
_state.x = x < 0 ? 0 : x > maxX ? maxX : x;
|
|
1287
1306
|
_state.y = y < 0 ? 0 : y > maxY ? maxY : y;
|
|
1288
1307
|
_state.progressX = maxX > 0 ? _state.x / maxX : 0;
|
|
@@ -1290,10 +1309,10 @@ function createScroll(options) {
|
|
|
1290
1309
|
}
|
|
1291
1310
|
function measure() {
|
|
1292
1311
|
if (stopped || !listenersAttached) return;
|
|
1293
|
-
const scrollWidth =
|
|
1294
|
-
const clientWidth =
|
|
1295
|
-
const scrollHeight =
|
|
1296
|
-
const clientHeight =
|
|
1312
|
+
const scrollWidth = scroller.scrollWidth;
|
|
1313
|
+
const clientWidth = scroller.clientWidth;
|
|
1314
|
+
const scrollHeight = scroller.scrollHeight;
|
|
1315
|
+
const clientHeight = scroller.clientHeight;
|
|
1297
1316
|
const maxX = scrollWidth - clientWidth;
|
|
1298
1317
|
const maxY = scrollHeight - clientHeight;
|
|
1299
1318
|
_state.maxX = maxX > 0 ? maxX : 0;
|
|
@@ -1305,7 +1324,14 @@ function createScroll(options) {
|
|
|
1305
1324
|
}
|
|
1306
1325
|
function flush() {
|
|
1307
1326
|
rafId = 0;
|
|
1308
|
-
if (
|
|
1327
|
+
if (stopped) return;
|
|
1328
|
+
if (needsMeasure) {
|
|
1329
|
+
needsMeasure = false;
|
|
1330
|
+
dirty = false;
|
|
1331
|
+
measure();
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
if (!dirty) return;
|
|
1309
1335
|
dirty = false;
|
|
1310
1336
|
computePosition();
|
|
1311
1337
|
onScroll(_state);
|
|
@@ -1320,6 +1346,7 @@ function createScroll(options) {
|
|
|
1320
1346
|
rafId = 0;
|
|
1321
1347
|
}
|
|
1322
1348
|
dirty = false;
|
|
1349
|
+
needsMeasure = false;
|
|
1323
1350
|
}
|
|
1324
1351
|
function onScrollEvent() {
|
|
1325
1352
|
if (stopped) return;
|
|
@@ -1328,26 +1355,33 @@ function createScroll(options) {
|
|
|
1328
1355
|
}
|
|
1329
1356
|
function onROResize() {
|
|
1330
1357
|
if (stopped) return;
|
|
1358
|
+
if (pageDoc) {
|
|
1359
|
+
needsMeasure = true;
|
|
1360
|
+
scheduleFlush();
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1331
1363
|
measure();
|
|
1332
1364
|
}
|
|
1333
1365
|
function attachListeners() {
|
|
1334
1366
|
if (listenersAttached) return;
|
|
1335
1367
|
listenersAttached = true;
|
|
1336
|
-
|
|
1337
|
-
unobserveRO = observeResize(
|
|
1368
|
+
target.addEventListener("scroll", onScrollEvent, { passive: true });
|
|
1369
|
+
unobserveRO = observeResize(scroller, onROResize);
|
|
1370
|
+
if (pageDoc) window.addEventListener("resize", onROResize);
|
|
1338
1371
|
measure();
|
|
1339
1372
|
}
|
|
1340
1373
|
function detachListeners() {
|
|
1341
1374
|
if (!listenersAttached) return;
|
|
1342
1375
|
listenersAttached = false;
|
|
1343
|
-
|
|
1376
|
+
target.removeEventListener("scroll", onScrollEvent);
|
|
1344
1377
|
unobserveRO?.();
|
|
1345
1378
|
unobserveRO = void 0;
|
|
1379
|
+
if (pageDoc) window.removeEventListener("resize", onROResize);
|
|
1346
1380
|
cancelFlush();
|
|
1347
1381
|
}
|
|
1348
1382
|
let cleanupVisibility;
|
|
1349
1383
|
if (visibility === "pause") {
|
|
1350
|
-
let elementInView =
|
|
1384
|
+
let elementInView = pageDoc !== void 0;
|
|
1351
1385
|
let documentVisible = !document.hidden;
|
|
1352
1386
|
function recompute() {
|
|
1353
1387
|
if (stopped) return;
|
|
@@ -1371,8 +1405,8 @@ function createScroll(options) {
|
|
|
1371
1405
|
}
|
|
1372
1406
|
document.addEventListener("visibilitychange", onVisChange);
|
|
1373
1407
|
window.addEventListener("pageshow", onPageShow);
|
|
1374
|
-
const unobserveIO = observeIntersection({
|
|
1375
|
-
element,
|
|
1408
|
+
const unobserveIO = pageDoc ? void 0 : observeIntersection({
|
|
1409
|
+
element: scroller,
|
|
1376
1410
|
onIntersect: (entry) => {
|
|
1377
1411
|
elementInView = entry.isIntersecting;
|
|
1378
1412
|
recompute();
|
|
@@ -1382,8 +1416,9 @@ function createScroll(options) {
|
|
|
1382
1416
|
cleanupVisibility = () => {
|
|
1383
1417
|
document.removeEventListener("visibilitychange", onVisChange);
|
|
1384
1418
|
window.removeEventListener("pageshow", onPageShow);
|
|
1385
|
-
unobserveIO();
|
|
1419
|
+
unobserveIO?.();
|
|
1386
1420
|
};
|
|
1421
|
+
recompute();
|
|
1387
1422
|
} else {
|
|
1388
1423
|
setPhase("tracking", "started");
|
|
1389
1424
|
attachListeners();
|
|
@@ -1413,6 +1448,10 @@ function createScroll(options) {
|
|
|
1413
1448
|
stop
|
|
1414
1449
|
};
|
|
1415
1450
|
}
|
|
1451
|
+
const DOCUMENT_NODE = 9;
|
|
1452
|
+
function isDocument(target) {
|
|
1453
|
+
return target.nodeType === DOCUMENT_NODE;
|
|
1454
|
+
}
|
|
1416
1455
|
//#endregion
|
|
1417
1456
|
//#region src/core/throttle/index.ts
|
|
1418
1457
|
/**
|
|
@@ -1616,6 +1655,6 @@ function createDebounce(options) {
|
|
|
1616
1655
|
};
|
|
1617
1656
|
}
|
|
1618
1657
|
//#endregion
|
|
1619
|
-
export {
|
|
1658
|
+
export { isPhaseError as C, linkAbortSignal as E, invalidDurationError as S, serverContextError 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, missingContextError as w, conflictingTargetError as x, createTicker as y };
|
|
1620
1659
|
|
|
1621
|
-
//# sourceMappingURL=debounce-
|
|
1660
|
+
//# sourceMappingURL=debounce-V_5xqZSY.js.map
|