@ansh-ui/core 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/LICENSE +21 -0
- package/dist/behaviours.cjs +64 -0
- package/dist/behaviours.cjs.map +1 -0
- package/dist/behaviours.d.cts +3 -0
- package/dist/behaviours.d.ts +3 -0
- package/dist/behaviours.js +7 -0
- package/dist/behaviours.js.map +1 -0
- package/dist/chunk-23GSSIMK.cjs +439 -0
- package/dist/chunk-23GSSIMK.cjs.map +1 -0
- package/dist/chunk-56NNBIAO.js +2446 -0
- package/dist/chunk-56NNBIAO.js.map +1 -0
- package/dist/chunk-6PFYGJWD.cjs +136 -0
- package/dist/chunk-6PFYGJWD.cjs.map +1 -0
- package/dist/chunk-7MEIXWPH.js +313 -0
- package/dist/chunk-7MEIXWPH.js.map +1 -0
- package/dist/chunk-7MYTSAPR.js +83 -0
- package/dist/chunk-7MYTSAPR.js.map +1 -0
- package/dist/chunk-BOQQHICI.js +1036 -0
- package/dist/chunk-BOQQHICI.js.map +1 -0
- package/dist/chunk-C6XPQO2O.js +1170 -0
- package/dist/chunk-C6XPQO2O.js.map +1 -0
- package/dist/chunk-DXJ63QAR.cjs +723 -0
- package/dist/chunk-DXJ63QAR.cjs.map +1 -0
- package/dist/chunk-FAVIEION.js +705 -0
- package/dist/chunk-FAVIEION.js.map +1 -0
- package/dist/chunk-FNR35HTL.cjs +107 -0
- package/dist/chunk-FNR35HTL.cjs.map +1 -0
- package/dist/chunk-GHBRGMSO.cjs +91 -0
- package/dist/chunk-GHBRGMSO.cjs.map +1 -0
- package/dist/chunk-GJRIHN76.cjs +359 -0
- package/dist/chunk-GJRIHN76.cjs.map +1 -0
- package/dist/chunk-ILKUL7GD.cjs +115 -0
- package/dist/chunk-ILKUL7GD.cjs.map +1 -0
- package/dist/chunk-KTUBCQC3.js +409 -0
- package/dist/chunk-KTUBCQC3.js.map +1 -0
- package/dist/chunk-KU2C6GEA.js +90 -0
- package/dist/chunk-KU2C6GEA.js.map +1 -0
- package/dist/chunk-LGA3N3YJ.cjs +413 -0
- package/dist/chunk-LGA3N3YJ.cjs.map +1 -0
- package/dist/chunk-MMO7KO53.cjs +1060 -0
- package/dist/chunk-MMO7KO53.cjs.map +1 -0
- package/dist/chunk-NSXKCJJ7.js +109 -0
- package/dist/chunk-NSXKCJJ7.js.map +1 -0
- package/dist/chunk-QALZKHWH.js +387 -0
- package/dist/chunk-QALZKHWH.js.map +1 -0
- package/dist/chunk-R7UJ7MWP.cjs +2479 -0
- package/dist/chunk-R7UJ7MWP.cjs.map +1 -0
- package/dist/chunk-UJBXY6QC.cjs +1186 -0
- package/dist/chunk-UJBXY6QC.cjs.map +1 -0
- package/dist/chunk-YFQXRC5R.js +99 -0
- package/dist/chunk-YFQXRC5R.js.map +1 -0
- package/dist/color.cjs +257 -0
- package/dist/color.cjs.map +1 -0
- package/dist/color.d.cts +303 -0
- package/dist/color.d.ts +303 -0
- package/dist/color.js +8 -0
- package/dist/color.js.map +1 -0
- package/dist/easing.cjs +189 -0
- package/dist/easing.cjs.map +1 -0
- package/dist/easing.d.cts +134 -0
- package/dist/easing.d.ts +134 -0
- package/dist/easing.js +4 -0
- package/dist/easing.js.map +1 -0
- package/dist/effects.cjs +99 -0
- package/dist/effects.cjs.map +1 -0
- package/dist/effects.d.cts +5 -0
- package/dist/effects.d.ts +5 -0
- package/dist/effects.js +10 -0
- package/dist/effects.js.map +1 -0
- package/dist/index-CCdpefMp.d.ts +967 -0
- package/dist/index-CQEEPu-D.d.ts +435 -0
- package/dist/index-DHOfsEIn.d.cts +967 -0
- package/dist/index-Dl8GCsya.d.cts +435 -0
- package/dist/index.cjs +2206 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1122 -0
- package/dist/index.d.ts +1122 -0
- package/dist/index.js +1182 -0
- package/dist/index.js.map +1 -0
- package/dist/math.cjs +238 -0
- package/dist/math.cjs.map +1 -0
- package/dist/math.d.cts +120 -0
- package/dist/math.d.ts +120 -0
- package/dist/math.js +5 -0
- package/dist/math.js.map +1 -0
- package/dist/noise.cjs +78 -0
- package/dist/noise.cjs.map +1 -0
- package/dist/noise.d.cts +190 -0
- package/dist/noise.d.ts +190 -0
- package/dist/noise.js +5 -0
- package/dist/noise.js.map +1 -0
- package/dist/palette-6HkVltYU.d.cts +219 -0
- package/dist/palette-6HkVltYU.d.ts +219 -0
- package/dist/random--4mSc5Lp.d.cts +75 -0
- package/dist/random--4mSc5Lp.d.ts +75 -0
- package/dist/reduced-motion-D0LUgZle.d.cts +120 -0
- package/dist/reduced-motion-D0LUgZle.d.ts +120 -0
- package/dist/types-B1FLLf3-.d.cts +21 -0
- package/dist/types-B1FLLf3-.d.ts +21 -0
- package/package.json +78 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1182 @@
|
|
|
1
|
+
export { addScaledVec2, addVec2, addVec2Into, angleOfVec2, cloneVec2, crossVec2, distanceSqVec2, distanceVec2, dotVec2, fromAngle, lengthSqVec2, lengthVec2, lerpVec2, lerpVec2Into, limitVec2, normalizeInRect, normalizeVec2, rectContains, reflectVec2, rotateVec2, scaleVec2, scaleVec2Into, setVec2, subVec2, subVec2Into, vec2 } from './chunk-NSXKCJJ7.js';
|
|
2
|
+
export { checkContrast, contrastRatio, ensureContrast, perceivedLightness, pickReadable, requiredRatio, worstContrast } from './chunk-7MYTSAPR.js';
|
|
3
|
+
export { GradientCache, ParticleBuffer, SpatialGrid, ageAndCull, animatedGradient, aurora, blob, constellation, createSurface, defineEffect, drawDot, drawGlow, effects, fireflies, integrate, meshGradient, meteors, mountEffect, noiseLayer, particleField, rain, resetFilterSupport, snow, spotlight, supportsCanvasFilter, supportsOffscreenCanvas, supportsWorkerRendering, traceClosedSpline, traceOpenSpline, traceRoundRect, waves, wrapBounds } from './chunk-56NNBIAO.js';
|
|
4
|
+
import { toRgbString, mixParsed } from './chunk-KTUBCQC3.js';
|
|
5
|
+
export { applyGradientStops, blend, composite, createGradient, createScale, expandPalette, fadeBy, fromUint32, gamutError, gamutMapOklch, generatePalette, harmony, inSrgbGamut, invertForSurface, isGamutStable, lerpHue, maxChroma, mixColors, mixParsed, oklchToRgbMapped, palettes, resolvePalette, sortByLightness, toHex, toHslString, toOklchString, toRgbString, toUint32, withAlpha } from './chunk-KTUBCQC3.js';
|
|
6
|
+
export { FRACTAL_DEFAULTS, GRAD3, GRAD4, clearNoiseTableCache, createNoise, createNoiseTables, createPerlinNoise, createSimplexNoise, createValueNoise, curl2D, curl2DAnimated, fbm2D, fbm3D, ridged2D, tileable2D, turbulence2D, warp2D } from './chunk-FAVIEION.js';
|
|
7
|
+
export { createRng, deriveSeed, goldenRatioSequence, hashString, jitter, r2Sequence, weightedPick } from './chunk-YFQXRC5R.js';
|
|
8
|
+
import { stagger } from './chunk-C6XPQO2O.js';
|
|
9
|
+
export { behaviours, captureStyles, defineBehaviour, getSplitResult, magnetic, marquee, mountBehaviour, parallax, ripple, scrollReveal, split, splitText, stagger, staggerDuration, tilt } from './chunk-C6XPQO2O.js';
|
|
10
|
+
import { now, prefersReducedMotion, getTicker, observeResize } from './chunk-BOQQHICI.js';
|
|
11
|
+
export { CapabilityError, Clock, FRAME_PHASES, FrameBudget, Registry, Ticker, getReducedMotionMode, getRegistry, getTicker, now, observeIntersection, observeResize, observerPoolStats, onReducedMotionChange, prefersReducedMotion, resetObserverPools, resetReducedMotion, resetRegistry, resetTicker, resolveMotion, setReducedMotionMode, setTicker, trackPointer } from './chunk-BOQQHICI.js';
|
|
12
|
+
import { parseColor, tryParseColor } from './chunk-QALZKHWH.js';
|
|
13
|
+
export { ColorParseError, clearColorCache, hslToRgb, hsvToRgb, linearToOklab, linearToRgb, linearToSrgbChannel, listNamedColors, namedColorToHex, oklabToLinear, oklabToOklch, oklabToRgb, oklchToOklab, oklchToRgb, parseColor, relativeLuminance, resolveComputed, resolveCssVariable, rgbToHsl, rgbToHsv, rgbToLinear, rgbToOklab, rgbToOklch, srgbToLinearChannel, tryParseColor } from './chunk-QALZKHWH.js';
|
|
14
|
+
import { resolveEasing } from './chunk-7MEIXWPH.js';
|
|
15
|
+
export { anticipate, cubicBezier, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, easings, linear, listEasings, mirrorEasing, power, readEasingTable, registerEasing, resolveEasing, reverseEasing, sampleEasing, signature, steps, unregisterEasing } from './chunk-7MEIXWPH.js';
|
|
16
|
+
import { clamp, lerp } from './chunk-KU2C6GEA.js';
|
|
17
|
+
export { EPSILON, HALF_PI, PI, TAU, angleDelta, approximately, clamp, damp, degrees, finite, inverseLerp, lerp, lerpAngle, mean, moveTowards, percentileSorted, pingPong, radians, remap, roundTo, smootherstep, smoothstep, snap, wrap } from './chunk-KU2C6GEA.js';
|
|
18
|
+
|
|
19
|
+
// src/spring/solver.ts
|
|
20
|
+
var SPRING_DEFAULTS = {
|
|
21
|
+
stiffness: 170,
|
|
22
|
+
damping: 26,
|
|
23
|
+
mass: 1,
|
|
24
|
+
duration: 0.6,
|
|
25
|
+
bounce: 0,
|
|
26
|
+
restDelta: 0.01,
|
|
27
|
+
restSpeed: 0.01,
|
|
28
|
+
from: 0,
|
|
29
|
+
to: 1,
|
|
30
|
+
velocity: 0
|
|
31
|
+
};
|
|
32
|
+
var MAX_DURATION = 10;
|
|
33
|
+
var MAX_DAMPING_RATIO = 20;
|
|
34
|
+
function perceptualToPhysical(duration, bounce, mass = 1) {
|
|
35
|
+
const safeDuration = Math.max(0.01, duration);
|
|
36
|
+
const b = clamp(bounce, -1, 1);
|
|
37
|
+
const dampingRatio = b >= 0 ? 1 - b : Math.min(MAX_DAMPING_RATIO, 1 / Math.max(1 / MAX_DAMPING_RATIO, 1 + b));
|
|
38
|
+
const omega = 2 * Math.PI / safeDuration;
|
|
39
|
+
return {
|
|
40
|
+
stiffness: omega * omega * mass,
|
|
41
|
+
damping: 2 * dampingRatio * omega * mass,
|
|
42
|
+
mass
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function physicalToPerceptual(stiffness, damping, mass = 1) {
|
|
46
|
+
const omega = Math.sqrt(stiffness / mass);
|
|
47
|
+
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
|
48
|
+
return {
|
|
49
|
+
duration: 2 * Math.PI / omega,
|
|
50
|
+
bounce: dampingRatio <= 1 ? 1 - dampingRatio : 1 / dampingRatio - 1
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function createSpring(options = {}) {
|
|
54
|
+
const mass = Math.max(0.01, options.mass ?? SPRING_DEFAULTS.mass);
|
|
55
|
+
const usesPerceptual = options.duration !== void 0 || options.bounce !== void 0;
|
|
56
|
+
const { stiffness, damping } = usesPerceptual ? perceptualToPhysical(
|
|
57
|
+
options.duration ?? SPRING_DEFAULTS.duration,
|
|
58
|
+
options.bounce ?? SPRING_DEFAULTS.bounce,
|
|
59
|
+
mass
|
|
60
|
+
) : {
|
|
61
|
+
stiffness: Math.max(0.01, options.stiffness ?? SPRING_DEFAULTS.stiffness),
|
|
62
|
+
damping: Math.max(0, options.damping ?? SPRING_DEFAULTS.damping)
|
|
63
|
+
};
|
|
64
|
+
const from = options.from ?? SPRING_DEFAULTS.from;
|
|
65
|
+
const to = options.to ?? SPRING_DEFAULTS.to;
|
|
66
|
+
const v0 = options.velocity ?? SPRING_DEFAULTS.velocity;
|
|
67
|
+
const restDelta = Math.max(1e-9, options.restDelta ?? SPRING_DEFAULTS.restDelta);
|
|
68
|
+
const restSpeed = Math.max(1e-9, options.restSpeed ?? SPRING_DEFAULTS.restSpeed);
|
|
69
|
+
const omega0 = Math.sqrt(stiffness / mass);
|
|
70
|
+
const zeta = damping / (2 * Math.sqrt(stiffness * mass));
|
|
71
|
+
const delta = to - from;
|
|
72
|
+
let evaluate;
|
|
73
|
+
if (delta === 0 && v0 === 0) {
|
|
74
|
+
evaluate = () => ({ y: 0, dy: 0 });
|
|
75
|
+
} else if (zeta < 1 - 1e-9) {
|
|
76
|
+
const omegaD = omega0 * Math.sqrt(1 - zeta * zeta);
|
|
77
|
+
const c1 = delta;
|
|
78
|
+
const c2 = (-v0 + zeta * omega0 * delta) / omegaD;
|
|
79
|
+
evaluate = (t) => {
|
|
80
|
+
const envelope = Math.exp(-zeta * omega0 * t);
|
|
81
|
+
const cos = Math.cos(omegaD * t);
|
|
82
|
+
const sin = Math.sin(omegaD * t);
|
|
83
|
+
const y = envelope * (c1 * cos + c2 * sin);
|
|
84
|
+
const dy = envelope * (-zeta * omega0 * (c1 * cos + c2 * sin) + omegaD * (c2 * cos - c1 * sin));
|
|
85
|
+
return { y, dy };
|
|
86
|
+
};
|
|
87
|
+
} else if (zeta <= 1 + 1e-9) {
|
|
88
|
+
const c1 = delta;
|
|
89
|
+
const c2 = -v0 + omega0 * delta;
|
|
90
|
+
evaluate = (t) => {
|
|
91
|
+
const envelope = Math.exp(-omega0 * t);
|
|
92
|
+
const y = envelope * (c1 + c2 * t);
|
|
93
|
+
const dy = envelope * (c2 - omega0 * (c1 + c2 * t));
|
|
94
|
+
return { y, dy };
|
|
95
|
+
};
|
|
96
|
+
} else {
|
|
97
|
+
const s = omega0 * Math.sqrt(zeta * zeta - 1);
|
|
98
|
+
const c1 = delta;
|
|
99
|
+
const c2 = (-v0 + zeta * omega0 * delta) / s;
|
|
100
|
+
evaluate = (t) => {
|
|
101
|
+
const envelope = Math.exp(-zeta * omega0 * t);
|
|
102
|
+
const cosh = Math.cosh(s * t);
|
|
103
|
+
const sinh = Math.sinh(s * t);
|
|
104
|
+
const y = envelope * (c1 * cosh + c2 * sinh);
|
|
105
|
+
const dy = envelope * (-zeta * omega0 * (c1 * cosh + c2 * sinh) + s * (c1 * sinh + c2 * cosh));
|
|
106
|
+
return { y, dy };
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const at = (t) => {
|
|
110
|
+
if (t <= 0) return { position: from, velocity: v0, done: false };
|
|
111
|
+
const { y, dy } = evaluate(t);
|
|
112
|
+
const position = to - y;
|
|
113
|
+
const velocity = -dy;
|
|
114
|
+
const done = Math.abs(y) <= restDelta && Math.abs(velocity) <= restSpeed;
|
|
115
|
+
return done ? { position: to, velocity: 0, done: true } : { position, velocity, done: false };
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
at,
|
|
119
|
+
duration: estimateDuration(at),
|
|
120
|
+
dampingRatio: zeta,
|
|
121
|
+
angularFrequency: omega0
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function estimateDuration(at) {
|
|
125
|
+
const STEP = 1 / 60;
|
|
126
|
+
let t = 0;
|
|
127
|
+
let previous = 0;
|
|
128
|
+
while (t < MAX_DURATION) {
|
|
129
|
+
previous = t;
|
|
130
|
+
t += STEP;
|
|
131
|
+
if (at(t).done) break;
|
|
132
|
+
}
|
|
133
|
+
if (t >= MAX_DURATION) return MAX_DURATION;
|
|
134
|
+
let low = previous;
|
|
135
|
+
let high = t;
|
|
136
|
+
for (let i = 0; i < 12; i++) {
|
|
137
|
+
const mid = (low + high) / 2;
|
|
138
|
+
if (at(mid).done) high = mid;
|
|
139
|
+
else low = mid;
|
|
140
|
+
}
|
|
141
|
+
return high;
|
|
142
|
+
}
|
|
143
|
+
function createDecay(options = {}) {
|
|
144
|
+
const from = options.from ?? 0;
|
|
145
|
+
const velocity = options.velocity ?? 0;
|
|
146
|
+
const power2 = options.power ?? 0.8;
|
|
147
|
+
const timeConstant = options.timeConstant ?? 0.325;
|
|
148
|
+
const restDelta = options.restDelta ?? 0.5;
|
|
149
|
+
const amplitude = power2 * velocity;
|
|
150
|
+
let target = from + amplitude;
|
|
151
|
+
if (options.min !== void 0) target = Math.max(options.min, target);
|
|
152
|
+
if (options.max !== void 0) target = Math.min(options.max, target);
|
|
153
|
+
const resolvedAmplitude = target - from;
|
|
154
|
+
const at = (t) => {
|
|
155
|
+
if (t <= 0) return { position: from, velocity, done: false };
|
|
156
|
+
const decayFactor = Math.exp(-t / timeConstant);
|
|
157
|
+
const position = target - resolvedAmplitude * decayFactor;
|
|
158
|
+
const currentVelocity = resolvedAmplitude / timeConstant * decayFactor;
|
|
159
|
+
const done = Math.abs(target - position) <= restDelta;
|
|
160
|
+
return done ? { position: target, velocity: 0, done: true } : { position, velocity: currentVelocity, done: false };
|
|
161
|
+
};
|
|
162
|
+
return { at, duration: estimateDuration(at), target };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/spring/presets.ts
|
|
166
|
+
var springs = {
|
|
167
|
+
/** No overshoot, quick. The safe default for UI state changes. */
|
|
168
|
+
default: { duration: 0.5, bounce: 0 },
|
|
169
|
+
/** Fast and crisp. Menus, dropdowns, toggles. */
|
|
170
|
+
snappy: { duration: 0.35, bounce: 0.15 },
|
|
171
|
+
/** Lively overshoot. Playful confirmations, likes, badges. */
|
|
172
|
+
bouncy: { duration: 0.6, bounce: 0.4 },
|
|
173
|
+
/** Slow, deliberate, weighty. Large surfaces and sheets. */
|
|
174
|
+
heavy: { duration: 0.9, bounce: -0.2 },
|
|
175
|
+
/** Barely-there motion for micro-interactions. */
|
|
176
|
+
subtle: { duration: 0.25, bounce: 0 },
|
|
177
|
+
/** Long, luxurious settle. Hero content and page transitions. */
|
|
178
|
+
cinematic: { duration: 1.2, bounce: 0.1 },
|
|
179
|
+
/** Rubber-band feel for drag release. */
|
|
180
|
+
elastic: { duration: 0.8, bounce: 0.65 },
|
|
181
|
+
/** Molasses. Deliberately sluggish — use for depth cues, not for controls. */
|
|
182
|
+
molasses: { duration: 1.6, bounce: -0.4 }
|
|
183
|
+
};
|
|
184
|
+
function resolveSpringOptions(input) {
|
|
185
|
+
if (input === void 0) return springs.default;
|
|
186
|
+
if (typeof input === "string") return springs[input] ?? springs.default;
|
|
187
|
+
return input;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/kernel/lifecycle.ts
|
|
191
|
+
function attachLifecycle(ticker, options = {}) {
|
|
192
|
+
if (typeof document === "undefined" || typeof window === "undefined") return () => {
|
|
193
|
+
};
|
|
194
|
+
const pauseWhenHidden = options.pauseWhenHidden ?? true;
|
|
195
|
+
const pauseWhenBlurred = options.pauseWhenBlurred ?? false;
|
|
196
|
+
const respectBattery = options.respectBattery ?? true;
|
|
197
|
+
const threshold = options.batteryThreshold ?? 0.2;
|
|
198
|
+
const saveQuality = options.batterySaveQuality ?? 0.5;
|
|
199
|
+
const cleanups = [];
|
|
200
|
+
if (pauseWhenHidden) {
|
|
201
|
+
const onVisibility = () => {
|
|
202
|
+
if (document.hidden) ticker.pause("document-hidden");
|
|
203
|
+
else ticker.resume("document-hidden");
|
|
204
|
+
};
|
|
205
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
206
|
+
cleanups.push(() => document.removeEventListener("visibilitychange", onVisibility));
|
|
207
|
+
onVisibility();
|
|
208
|
+
}
|
|
209
|
+
if (pauseWhenBlurred) {
|
|
210
|
+
const onBlur = () => ticker.pause("window-blurred");
|
|
211
|
+
const onFocus = () => ticker.resume("window-blurred");
|
|
212
|
+
window.addEventListener("blur", onBlur);
|
|
213
|
+
window.addEventListener("focus", onFocus);
|
|
214
|
+
cleanups.push(() => {
|
|
215
|
+
window.removeEventListener("blur", onBlur);
|
|
216
|
+
window.removeEventListener("focus", onFocus);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (respectBattery) {
|
|
220
|
+
const getBattery = navigator.getBattery;
|
|
221
|
+
if (typeof getBattery === "function") {
|
|
222
|
+
let battery = null;
|
|
223
|
+
let disposed = false;
|
|
224
|
+
const apply = () => {
|
|
225
|
+
if (!battery) return;
|
|
226
|
+
const saving = !battery.charging && battery.level <= threshold;
|
|
227
|
+
if (saving) ticker.lockQuality(saveQuality);
|
|
228
|
+
};
|
|
229
|
+
getBattery.call(navigator).then((manager) => {
|
|
230
|
+
if (disposed) return;
|
|
231
|
+
battery = manager;
|
|
232
|
+
manager.addEventListener("levelchange", apply);
|
|
233
|
+
manager.addEventListener("chargingchange", apply);
|
|
234
|
+
cleanups.push(() => {
|
|
235
|
+
manager.removeEventListener("levelchange", apply);
|
|
236
|
+
manager.removeEventListener("chargingchange", apply);
|
|
237
|
+
});
|
|
238
|
+
apply();
|
|
239
|
+
}).catch(() => {
|
|
240
|
+
});
|
|
241
|
+
cleanups.push(() => {
|
|
242
|
+
disposed = true;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return () => {
|
|
247
|
+
for (const cleanup of cleanups) cleanup();
|
|
248
|
+
cleanups.length = 0;
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/motion/motion-value.ts
|
|
253
|
+
var VELOCITY_SAMPLE_WINDOW_MS = 100;
|
|
254
|
+
var MotionValue = class {
|
|
255
|
+
current;
|
|
256
|
+
previous;
|
|
257
|
+
listeners = /* @__PURE__ */ new Set();
|
|
258
|
+
renderListeners = /* @__PURE__ */ new Set();
|
|
259
|
+
/** Timestamp and value of the last update, for velocity. */
|
|
260
|
+
lastUpdated = 0;
|
|
261
|
+
lastValue;
|
|
262
|
+
velocityValue = 0;
|
|
263
|
+
/** Set while an `animate()` drives this value, so a new animation can cancel the old one. */
|
|
264
|
+
stopAnimation = null;
|
|
265
|
+
constructor(initial) {
|
|
266
|
+
this.current = initial;
|
|
267
|
+
this.previous = initial;
|
|
268
|
+
this.lastValue = initial;
|
|
269
|
+
this.lastUpdated = now();
|
|
270
|
+
}
|
|
271
|
+
get() {
|
|
272
|
+
return this.current;
|
|
273
|
+
}
|
|
274
|
+
getPrevious() {
|
|
275
|
+
return this.previous;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Write a new value and notify subscribers.
|
|
279
|
+
*
|
|
280
|
+
* Notification is synchronous. That is a deliberate choice: this is called from inside the
|
|
281
|
+
* ticker's `write` phase, and deferring would push DOM writes into the next frame, adding a
|
|
282
|
+
* frame of latency to every animation.
|
|
283
|
+
*/
|
|
284
|
+
set(value) {
|
|
285
|
+
if (value === this.current) return;
|
|
286
|
+
this.previous = this.current;
|
|
287
|
+
this.current = value;
|
|
288
|
+
this.updateVelocity(value);
|
|
289
|
+
for (const listener of this.renderListeners) listener(value);
|
|
290
|
+
for (const listener of this.listeners) listener(value, this.previous);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Write without notifying, then notify once.
|
|
294
|
+
* Used when several related values change together and should produce one visual update.
|
|
295
|
+
*/
|
|
296
|
+
setSilent(value) {
|
|
297
|
+
this.previous = this.current;
|
|
298
|
+
this.current = value;
|
|
299
|
+
this.updateVelocity(value);
|
|
300
|
+
}
|
|
301
|
+
updateVelocity(value) {
|
|
302
|
+
if (typeof value !== "number" || typeof this.lastValue !== "number") return;
|
|
303
|
+
const timestamp = now();
|
|
304
|
+
const elapsed = timestamp - this.lastUpdated;
|
|
305
|
+
if (elapsed > 1) {
|
|
306
|
+
this.velocityValue = (value - this.lastValue) / elapsed * 1e3;
|
|
307
|
+
this.lastValue = value;
|
|
308
|
+
this.lastUpdated = timestamp;
|
|
309
|
+
} else if (elapsed > VELOCITY_SAMPLE_WINDOW_MS) {
|
|
310
|
+
this.velocityValue = 0;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Current velocity in units per second.
|
|
315
|
+
*
|
|
316
|
+
* Returns 0 if the value hasn't changed recently, so a spring picking up from rest doesn't
|
|
317
|
+
* inherit a phantom velocity from an interaction that ended a second ago.
|
|
318
|
+
*/
|
|
319
|
+
getVelocity() {
|
|
320
|
+
if (now() - this.lastUpdated > VELOCITY_SAMPLE_WINDOW_MS) return 0;
|
|
321
|
+
return this.velocityValue;
|
|
322
|
+
}
|
|
323
|
+
/** Directly set velocity — used when handing off from a gesture to a spring. */
|
|
324
|
+
setVelocity(velocity) {
|
|
325
|
+
this.velocityValue = velocity;
|
|
326
|
+
this.lastUpdated = now();
|
|
327
|
+
}
|
|
328
|
+
/** Subscribe to changes. The listener is *not* called immediately. */
|
|
329
|
+
on(listener) {
|
|
330
|
+
this.listeners.add(listener);
|
|
331
|
+
return () => this.listeners.delete(listener);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Subscribe as a renderer — called before regular listeners.
|
|
335
|
+
*
|
|
336
|
+
* Framework bindings use this for the DOM write, so that visual output is never delayed by
|
|
337
|
+
* application-level subscribers.
|
|
338
|
+
*/
|
|
339
|
+
onRender(listener) {
|
|
340
|
+
this.renderListeners.add(listener);
|
|
341
|
+
return () => this.renderListeners.delete(listener);
|
|
342
|
+
}
|
|
343
|
+
get listenerCount() {
|
|
344
|
+
return this.listeners.size + this.renderListeners.size;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Register the currently-running animation so a subsequent one can interrupt it.
|
|
348
|
+
* Called by `animate()`; not part of the public surface.
|
|
349
|
+
*/
|
|
350
|
+
attachAnimation(stop) {
|
|
351
|
+
this.stopAnimation?.();
|
|
352
|
+
this.stopAnimation = stop;
|
|
353
|
+
}
|
|
354
|
+
/** Stop whatever is animating this value, leaving it wherever it is. */
|
|
355
|
+
stop() {
|
|
356
|
+
this.stopAnimation?.();
|
|
357
|
+
this.stopAnimation = null;
|
|
358
|
+
}
|
|
359
|
+
get isAnimating() {
|
|
360
|
+
return this.stopAnimation !== null;
|
|
361
|
+
}
|
|
362
|
+
/** Release listeners and stop any animation. */
|
|
363
|
+
destroy() {
|
|
364
|
+
this.stop();
|
|
365
|
+
this.listeners.clear();
|
|
366
|
+
this.renderListeners.clear();
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
function motionValue(initial) {
|
|
370
|
+
return new MotionValue(initial);
|
|
371
|
+
}
|
|
372
|
+
function derived(sources, compute) {
|
|
373
|
+
const read = () => sources.map((s) => s.get());
|
|
374
|
+
const output = new MotionValue(compute(...read()));
|
|
375
|
+
const unsubscribes = sources.map(
|
|
376
|
+
(source) => source.on(() => {
|
|
377
|
+
output.set(compute(...read()));
|
|
378
|
+
})
|
|
379
|
+
);
|
|
380
|
+
const originalDestroy = output.destroy.bind(output);
|
|
381
|
+
output.destroy = () => {
|
|
382
|
+
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
383
|
+
originalDestroy();
|
|
384
|
+
};
|
|
385
|
+
return output;
|
|
386
|
+
}
|
|
387
|
+
function mapValue(source, transform) {
|
|
388
|
+
const output = new MotionValue(transform(source.get()));
|
|
389
|
+
const unsubscribe = source.on((value) => output.set(transform(value)));
|
|
390
|
+
const originalDestroy = output.destroy.bind(output);
|
|
391
|
+
output.destroy = () => {
|
|
392
|
+
unsubscribe();
|
|
393
|
+
originalDestroy();
|
|
394
|
+
};
|
|
395
|
+
return output;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/motion/interpolate.ts
|
|
399
|
+
function interpolateNumber(from, to) {
|
|
400
|
+
const delta = to - from;
|
|
401
|
+
return (t) => from + delta * t;
|
|
402
|
+
}
|
|
403
|
+
function interpolateColor(from, to, space = "oklab") {
|
|
404
|
+
const a = parseColor(from);
|
|
405
|
+
const b = parseColor(to);
|
|
406
|
+
const options = { space };
|
|
407
|
+
return (t) => toRgbString(mixParsed(a, b, t, options));
|
|
408
|
+
}
|
|
409
|
+
var UNIT_PATTERN = /^(-?[\d.]+)([a-z%]*)$/i;
|
|
410
|
+
function interpolateUnitValue(from, to) {
|
|
411
|
+
const fromMatch = UNIT_PATTERN.exec(from.trim());
|
|
412
|
+
const toMatch = UNIT_PATTERN.exec(to.trim());
|
|
413
|
+
if (!fromMatch || !toMatch) return null;
|
|
414
|
+
const fromValue = Number.parseFloat(fromMatch[1]);
|
|
415
|
+
const toValue = Number.parseFloat(toMatch[1]);
|
|
416
|
+
if (!Number.isFinite(fromValue) || !Number.isFinite(toValue)) return null;
|
|
417
|
+
const fromUnit = fromMatch[2];
|
|
418
|
+
const toUnit = toMatch[2];
|
|
419
|
+
if (fromUnit !== toUnit) {
|
|
420
|
+
if (fromValue === 0) return (t) => `${toValue * t}${toUnit}`;
|
|
421
|
+
if (toValue === 0) return (t) => `${fromValue * (1 - t)}${fromUnit}`;
|
|
422
|
+
return (t) => t < 0.5 ? from : to;
|
|
423
|
+
}
|
|
424
|
+
const delta = toValue - fromValue;
|
|
425
|
+
return (t) => `${fromValue + delta * t}${fromUnit}`;
|
|
426
|
+
}
|
|
427
|
+
var NUMBER_PATTERN = /-?\d*\.?\d+(?:e[+-]?\d+)?/gi;
|
|
428
|
+
function interpolateNumericString(from, to) {
|
|
429
|
+
const fromNumbers = from.match(NUMBER_PATTERN);
|
|
430
|
+
const toNumbers = to.match(NUMBER_PATTERN);
|
|
431
|
+
if (!fromNumbers || !toNumbers || fromNumbers.length !== toNumbers.length) return null;
|
|
432
|
+
const template = from.split(NUMBER_PATTERN);
|
|
433
|
+
if (template.length !== fromNumbers.length + 1) return null;
|
|
434
|
+
const toTemplate = to.split(NUMBER_PATTERN);
|
|
435
|
+
for (let i = 0; i < template.length; i++) {
|
|
436
|
+
if (template[i] !== toTemplate[i]) return null;
|
|
437
|
+
}
|
|
438
|
+
const fromValues = fromNumbers.map(Number);
|
|
439
|
+
const toValues = toNumbers.map(Number);
|
|
440
|
+
if (fromValues.some(Number.isNaN) || toValues.some(Number.isNaN)) return null;
|
|
441
|
+
const count = fromValues.length;
|
|
442
|
+
return (t) => {
|
|
443
|
+
let out = "";
|
|
444
|
+
for (let i = 0; i < count; i++) {
|
|
445
|
+
out += template[i];
|
|
446
|
+
out += round(lerp(fromValues[i], toValues[i], t));
|
|
447
|
+
}
|
|
448
|
+
return out + template[count];
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function round(value) {
|
|
452
|
+
return Math.round(value * 1e4) / 1e4;
|
|
453
|
+
}
|
|
454
|
+
function interpolateArray(from, to, options) {
|
|
455
|
+
const interpolators = from.map(
|
|
456
|
+
(value, i) => resolveInterpolator(value, to[i] ?? value, options)
|
|
457
|
+
);
|
|
458
|
+
return (t) => interpolators.map((fn) => fn(t));
|
|
459
|
+
}
|
|
460
|
+
function interpolateObject(from, to, options) {
|
|
461
|
+
const keys = Object.keys(from);
|
|
462
|
+
const interpolators = /* @__PURE__ */ new Map();
|
|
463
|
+
for (const key of keys) {
|
|
464
|
+
const target = to[key] ?? from[key];
|
|
465
|
+
interpolators.set(key, resolveInterpolator(from[key], target, options));
|
|
466
|
+
}
|
|
467
|
+
return (t) => {
|
|
468
|
+
const out = {};
|
|
469
|
+
for (const key of keys) {
|
|
470
|
+
out[key] = interpolators.get(key)(t);
|
|
471
|
+
}
|
|
472
|
+
return out;
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function resolveInterpolator(from, to, options) {
|
|
476
|
+
if (typeof from === "number" && typeof to === "number") {
|
|
477
|
+
return interpolateNumber(from, to);
|
|
478
|
+
}
|
|
479
|
+
if (typeof from === "string" && typeof to === "string") {
|
|
480
|
+
if (looksLikeColor(from) && looksLikeColor(to)) {
|
|
481
|
+
return interpolateColor(from, to, options?.colorSpace);
|
|
482
|
+
}
|
|
483
|
+
const unit = interpolateUnitValue(from, to);
|
|
484
|
+
if (unit) return unit;
|
|
485
|
+
const numeric = interpolateNumericString(from, to);
|
|
486
|
+
if (numeric) return numeric;
|
|
487
|
+
return ((t) => t < 0.5 ? from : to);
|
|
488
|
+
}
|
|
489
|
+
if (Array.isArray(from) && Array.isArray(to)) {
|
|
490
|
+
return interpolateArray(from, to, options);
|
|
491
|
+
}
|
|
492
|
+
if (isPlainObject(from) && isPlainObject(to)) {
|
|
493
|
+
return interpolateObject(from, to, options);
|
|
494
|
+
}
|
|
495
|
+
return ((t) => t < 0.5 ? from : to);
|
|
496
|
+
}
|
|
497
|
+
function looksLikeColor(value) {
|
|
498
|
+
const v = value.trim();
|
|
499
|
+
if (v.startsWith("#")) return true;
|
|
500
|
+
if (/^(rgb|hsl|hsv|hsb|oklch|oklab|lab|lch|color)a?\(/i.test(v)) return true;
|
|
501
|
+
if (v === "transparent" || v === "currentcolor") return true;
|
|
502
|
+
if (/^[a-z]+$/i.test(v)) return tryParseColor(v) !== null;
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
function isPlainObject(value) {
|
|
506
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
507
|
+
}
|
|
508
|
+
function interpolateKeyframes(values, offsets, options) {
|
|
509
|
+
if (values.length === 0) throw new RangeError("[ansh] Keyframes require at least one value.");
|
|
510
|
+
if (values.length === 1) return () => values[0];
|
|
511
|
+
const positions = offsets && offsets.length === values.length ? offsets : values.map((_, i) => i / (values.length - 1));
|
|
512
|
+
const segments = values.slice(0, -1).map((value, i) => resolveInterpolator(value, values[i + 1], options));
|
|
513
|
+
return (t) => {
|
|
514
|
+
if (t <= positions[0]) return values[0];
|
|
515
|
+
const last = values.length - 1;
|
|
516
|
+
if (t >= positions[last]) return values[last];
|
|
517
|
+
let i = 0;
|
|
518
|
+
while (i < last - 1 && positions[i + 1] < t) i++;
|
|
519
|
+
const span = positions[i + 1] - positions[i];
|
|
520
|
+
const local = span <= 0 ? 0 : (t - positions[i]) / span;
|
|
521
|
+
return segments[i](local);
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/motion/sampler.ts
|
|
526
|
+
var DEFAULT_TWEEN_DURATION = 0.4;
|
|
527
|
+
function createSampler(from, to, options = {}) {
|
|
528
|
+
const type = options.type ?? (hasSpringOptions(options) ? "spring" : "tween");
|
|
529
|
+
const keyframes = Array.isArray(to) ? [from, ...to] : null;
|
|
530
|
+
const interpolator = keyframes ? interpolateKeyframes(keyframes, options.offsets, options) : resolveInterpolator(from, to, options);
|
|
531
|
+
if (type === "spring") {
|
|
532
|
+
const springOptions = {
|
|
533
|
+
...resolveSpringOptions(options.spring),
|
|
534
|
+
...defined("stiffness", options.stiffness),
|
|
535
|
+
...defined("damping", options.damping),
|
|
536
|
+
...defined("mass", options.mass),
|
|
537
|
+
...defined("bounce", options.bounce),
|
|
538
|
+
...defined("duration", options.duration),
|
|
539
|
+
velocity: options.velocity ?? 0
|
|
540
|
+
};
|
|
541
|
+
if (typeof from === "number" && typeof to === "number") {
|
|
542
|
+
const solver2 = createSpring({ ...springOptions, from, to });
|
|
543
|
+
return {
|
|
544
|
+
at: (elapsed) => solver2.at(Math.max(0, elapsed)).position,
|
|
545
|
+
duration: solver2.duration,
|
|
546
|
+
progressAt: (elapsed) => clamp(elapsed / Math.max(1e-6, solver2.duration), 0, 1)
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
const solver = createSpring({ ...springOptions, from: 0, to: 1 });
|
|
550
|
+
return {
|
|
551
|
+
at: (elapsed) => interpolator(solver.at(Math.max(0, elapsed)).position),
|
|
552
|
+
duration: solver.duration,
|
|
553
|
+
progressAt: (elapsed) => solver.at(Math.max(0, elapsed)).position
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
if (type === "decay") {
|
|
557
|
+
const decay = createDecay({
|
|
558
|
+
from: typeof from === "number" ? from : 0,
|
|
559
|
+
velocity: options.velocity ?? 0,
|
|
560
|
+
power: options.power,
|
|
561
|
+
timeConstant: options.timeConstant
|
|
562
|
+
});
|
|
563
|
+
return {
|
|
564
|
+
at: (elapsed) => decay.at(Math.max(0, elapsed)).position,
|
|
565
|
+
duration: decay.duration,
|
|
566
|
+
progressAt: (elapsed) => clamp(elapsed / Math.max(1e-6, decay.duration), 0, 1)
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
const easing = resolveEasing(options.easing);
|
|
570
|
+
const duration = Math.max(0, options.duration ?? DEFAULT_TWEEN_DURATION);
|
|
571
|
+
const progressAt = (elapsed) => duration === 0 ? 1 : easing(clamp(elapsed / duration, 0, 1));
|
|
572
|
+
return {
|
|
573
|
+
at: (elapsed) => interpolator(progressAt(elapsed)),
|
|
574
|
+
duration,
|
|
575
|
+
progressAt
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function hasSpringOptions(options) {
|
|
579
|
+
return options.spring !== void 0 || options.stiffness !== void 0 || options.damping !== void 0 || options.bounce !== void 0 || options.mass !== void 0;
|
|
580
|
+
}
|
|
581
|
+
function defined(key, value) {
|
|
582
|
+
return value === void 0 ? {} : { [key]: value };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/motion/animate.ts
|
|
586
|
+
function animate(from, to, options = {}) {
|
|
587
|
+
const motion = from instanceof MotionValue ? from : null;
|
|
588
|
+
const startValue = motion ? motion.get() : from;
|
|
589
|
+
const delay = Math.max(0, options.delay ?? 0);
|
|
590
|
+
const repeat = options.repeat ?? 0;
|
|
591
|
+
const repeatMode = options.repeatMode ?? "loop";
|
|
592
|
+
const repeatDelay = Math.max(0, options.repeatDelay ?? 0);
|
|
593
|
+
let resolveFinished = () => {
|
|
594
|
+
};
|
|
595
|
+
const finished = new Promise((resolve) => {
|
|
596
|
+
resolveFinished = resolve;
|
|
597
|
+
});
|
|
598
|
+
const emit = (value) => {
|
|
599
|
+
motion?.set(value);
|
|
600
|
+
options.onUpdate?.(value);
|
|
601
|
+
};
|
|
602
|
+
if (prefersReducedMotion() && (options.reducedMotion ?? "jump") === "jump") {
|
|
603
|
+
const finalValue = Array.isArray(to) ? to[to.length - 1] : to;
|
|
604
|
+
options.onStart?.();
|
|
605
|
+
emit(finalValue);
|
|
606
|
+
options.onComplete?.();
|
|
607
|
+
resolveFinished();
|
|
608
|
+
return {
|
|
609
|
+
stop: noop,
|
|
610
|
+
pause: noop,
|
|
611
|
+
play: noop,
|
|
612
|
+
seek: noop,
|
|
613
|
+
finish: noop,
|
|
614
|
+
isActive: false,
|
|
615
|
+
duration: 0,
|
|
616
|
+
finished
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
const sampler = createSampler(startValue, to, {
|
|
620
|
+
...options,
|
|
621
|
+
velocity: options.velocity ?? motion?.getVelocity() ?? 0
|
|
622
|
+
});
|
|
623
|
+
const cycleDuration = sampler.duration;
|
|
624
|
+
const cycleLength = cycleDuration + repeatDelay;
|
|
625
|
+
const activeDuration = repeat === Infinity ? Infinity : cycleDuration * (repeat + 1) + repeatDelay * repeat;
|
|
626
|
+
const totalDuration = repeat === Infinity ? Infinity : delay + activeDuration;
|
|
627
|
+
let elapsed = 0;
|
|
628
|
+
let paused = false;
|
|
629
|
+
let active = true;
|
|
630
|
+
let started = false;
|
|
631
|
+
const ticker = getTicker();
|
|
632
|
+
const applyCycle = (withinCycle, reversed) => {
|
|
633
|
+
const local = reversed ? Math.max(0, cycleDuration - withinCycle) : withinCycle;
|
|
634
|
+
emit(sampler.at(local));
|
|
635
|
+
};
|
|
636
|
+
const complete = () => {
|
|
637
|
+
if (!active) return;
|
|
638
|
+
active = false;
|
|
639
|
+
unsubscribe();
|
|
640
|
+
options.onComplete?.();
|
|
641
|
+
resolveFinished();
|
|
642
|
+
};
|
|
643
|
+
const frame = ({ delta }) => {
|
|
644
|
+
if (paused || !active) return;
|
|
645
|
+
elapsed += delta;
|
|
646
|
+
if (elapsed < delay) return;
|
|
647
|
+
if (!started) {
|
|
648
|
+
started = true;
|
|
649
|
+
options.onStart?.();
|
|
650
|
+
}
|
|
651
|
+
const sinceStart = elapsed - delay;
|
|
652
|
+
if (sinceStart >= activeDuration) {
|
|
653
|
+
applyCycle(cycleDuration, shouldReverse(repeat, repeatMode));
|
|
654
|
+
complete();
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const cycleIndex = cycleLength > 0 ? Math.floor(sinceStart / cycleLength) : 0;
|
|
658
|
+
const withinCycle = cycleLength > 0 ? sinceStart - cycleIndex * cycleLength : sinceStart;
|
|
659
|
+
applyCycle(Math.min(withinCycle, cycleDuration), shouldReverse(cycleIndex, repeatMode));
|
|
660
|
+
};
|
|
661
|
+
const unsubscribe = ticker.add(frame, { phase: "compute", name: "animate" });
|
|
662
|
+
const abort = () => {
|
|
663
|
+
if (!active) return;
|
|
664
|
+
active = false;
|
|
665
|
+
unsubscribe();
|
|
666
|
+
options.onStop?.();
|
|
667
|
+
resolveFinished();
|
|
668
|
+
};
|
|
669
|
+
motion?.attachAnimation(abort);
|
|
670
|
+
if (delay === 0) applyCycle(0, false);
|
|
671
|
+
return {
|
|
672
|
+
stop: abort,
|
|
673
|
+
pause() {
|
|
674
|
+
paused = true;
|
|
675
|
+
},
|
|
676
|
+
play() {
|
|
677
|
+
paused = false;
|
|
678
|
+
},
|
|
679
|
+
seek(time) {
|
|
680
|
+
elapsed = Math.max(0, time);
|
|
681
|
+
const sinceStart = Math.max(0, elapsed - delay);
|
|
682
|
+
if (sinceStart >= activeDuration) {
|
|
683
|
+
applyCycle(cycleDuration, shouldReverse(repeat, repeatMode));
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
const cycleIndex = cycleLength > 0 ? Math.floor(sinceStart / cycleLength) : 0;
|
|
687
|
+
const withinCycle = cycleLength > 0 ? sinceStart - cycleIndex * cycleLength : sinceStart;
|
|
688
|
+
applyCycle(Math.min(withinCycle, cycleDuration), shouldReverse(cycleIndex, repeatMode));
|
|
689
|
+
},
|
|
690
|
+
finish() {
|
|
691
|
+
if (!active) return;
|
|
692
|
+
applyCycle(cycleDuration, shouldReverse(repeat === Infinity ? 0 : repeat, repeatMode));
|
|
693
|
+
complete();
|
|
694
|
+
},
|
|
695
|
+
get isActive() {
|
|
696
|
+
return active;
|
|
697
|
+
},
|
|
698
|
+
duration: totalDuration,
|
|
699
|
+
finished
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function shouldReverse(index, mode) {
|
|
703
|
+
return (mode === "reverse" || mode === "mirror") && index % 2 === 1;
|
|
704
|
+
}
|
|
705
|
+
function noop() {
|
|
706
|
+
}
|
|
707
|
+
function springTo(value, getTarget, options = {}) {
|
|
708
|
+
const stiffness = options.stiffness ?? 170;
|
|
709
|
+
const damping = options.damping ?? 26;
|
|
710
|
+
const mass = Math.max(0.01, options.mass ?? 1);
|
|
711
|
+
const restDelta = options.restDelta ?? 0.01;
|
|
712
|
+
let velocity = options.velocity ?? 0;
|
|
713
|
+
const ticker = getTicker();
|
|
714
|
+
return ticker.add(
|
|
715
|
+
({ delta }) => {
|
|
716
|
+
const target = getTarget();
|
|
717
|
+
const current = value.get();
|
|
718
|
+
if (Math.abs(current - target) < restDelta && Math.abs(velocity) < restDelta) {
|
|
719
|
+
if (current !== target) value.set(target);
|
|
720
|
+
velocity = 0;
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
const steps2 = Math.min(4, Math.max(1, Math.ceil(delta / (1 / 60))));
|
|
724
|
+
const dt = delta / steps2;
|
|
725
|
+
let position = current;
|
|
726
|
+
for (let i = 0; i < steps2; i++) {
|
|
727
|
+
const acceleration = (-stiffness * (position - target) - damping * velocity) / mass;
|
|
728
|
+
velocity += acceleration * dt;
|
|
729
|
+
position += velocity * dt;
|
|
730
|
+
}
|
|
731
|
+
value.set(position);
|
|
732
|
+
value.setVelocity(velocity);
|
|
733
|
+
},
|
|
734
|
+
{ phase: "compute", name: options.name ?? "springTo" }
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/motion/timeline.ts
|
|
739
|
+
function createTimeline(options = {}) {
|
|
740
|
+
const entries = [];
|
|
741
|
+
const labels = /* @__PURE__ */ new Map();
|
|
742
|
+
const repeat = options.repeat ?? 0;
|
|
743
|
+
const yoyo = options.yoyo ?? false;
|
|
744
|
+
const autoplay = options.autoplay ?? true;
|
|
745
|
+
const respectReducedMotion = options.respectReducedMotion ?? true;
|
|
746
|
+
let totalDuration = 0;
|
|
747
|
+
let previousStart = 0;
|
|
748
|
+
let previousEnd = 0;
|
|
749
|
+
let currentTime = 0;
|
|
750
|
+
let playing = false;
|
|
751
|
+
let scale = options.timeScale ?? 1;
|
|
752
|
+
let iteration = 0;
|
|
753
|
+
let killed = false;
|
|
754
|
+
let subscription = null;
|
|
755
|
+
let resolveFinished = () => {
|
|
756
|
+
};
|
|
757
|
+
const finished = new Promise((resolve) => {
|
|
758
|
+
resolveFinished = resolve;
|
|
759
|
+
});
|
|
760
|
+
const RELATIVE = /^([<>]?)(?:([+-]=)\s*([\d.]+))?$/;
|
|
761
|
+
const LABEL_RELATIVE = /^(.+?)(?:([+-]=)\s*([\d.]+))?$/;
|
|
762
|
+
function resolvePosition(position) {
|
|
763
|
+
if (position === void 0) return previousEnd;
|
|
764
|
+
if (typeof position === "number") return Math.max(0, position);
|
|
765
|
+
const trimmed = position.trim();
|
|
766
|
+
const relative = RELATIVE.exec(trimmed);
|
|
767
|
+
if (relative) {
|
|
768
|
+
const anchor = relative[1] === "<" ? previousStart : previousEnd;
|
|
769
|
+
if (!relative[2]) return anchor;
|
|
770
|
+
const amount = Number.parseFloat(relative[3]);
|
|
771
|
+
return Math.max(0, relative[2] === "+=" ? anchor + amount : anchor - amount);
|
|
772
|
+
}
|
|
773
|
+
const labelMatch = LABEL_RELATIVE.exec(trimmed);
|
|
774
|
+
if (labelMatch) {
|
|
775
|
+
const base = labels.get(labelMatch[1]);
|
|
776
|
+
if (base !== void 0) {
|
|
777
|
+
if (!labelMatch[2]) return base;
|
|
778
|
+
const amount = Number.parseFloat(labelMatch[3]);
|
|
779
|
+
return Math.max(0, labelMatch[2] === "+=" ? base + amount : base - amount);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
console.warn(
|
|
783
|
+
`[ansh] Unknown timeline position "${position}". Placing at the end of the previous entry. Valid forms: a number, '<', '>', '+=0.2', '-=0.2', or a label name.`
|
|
784
|
+
);
|
|
785
|
+
return previousEnd;
|
|
786
|
+
}
|
|
787
|
+
function push(entry) {
|
|
788
|
+
entries.push(entry);
|
|
789
|
+
previousStart = entry.start;
|
|
790
|
+
previousEnd = entry.start + entry.duration;
|
|
791
|
+
if (previousEnd > totalDuration) totalDuration = previousEnd;
|
|
792
|
+
if (autoplay && !playing && !killed) api.play();
|
|
793
|
+
}
|
|
794
|
+
function applyAt(time, previousTime) {
|
|
795
|
+
for (const entry of entries) {
|
|
796
|
+
if (entry.isCallback) {
|
|
797
|
+
const crossedForward = previousTime < entry.start && time >= entry.start;
|
|
798
|
+
const crossedBackward = previousTime > entry.start && time <= entry.start;
|
|
799
|
+
if (crossedForward || crossedBackward) {
|
|
800
|
+
if (!entry.fired) {
|
|
801
|
+
entry.fired = true;
|
|
802
|
+
entry.apply(0);
|
|
803
|
+
}
|
|
804
|
+
} else if (Math.abs(time - entry.start) > 1e-6) {
|
|
805
|
+
entry.fired = false;
|
|
806
|
+
}
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
const local = clamp(time - entry.start, 0, entry.duration);
|
|
810
|
+
entry.apply(local);
|
|
811
|
+
}
|
|
812
|
+
options.onUpdate?.(time, totalDuration === 0 ? 1 : clamp(time / totalDuration, 0, 1));
|
|
813
|
+
}
|
|
814
|
+
function frame({ delta }) {
|
|
815
|
+
if (!playing || killed) return;
|
|
816
|
+
const previousTime = currentTime;
|
|
817
|
+
currentTime += delta * scale;
|
|
818
|
+
if (scale >= 0 && currentTime >= totalDuration) {
|
|
819
|
+
if (repeat === Infinity || iteration < repeat) {
|
|
820
|
+
iteration++;
|
|
821
|
+
options.onRepeat?.(iteration);
|
|
822
|
+
if (yoyo) {
|
|
823
|
+
currentTime = totalDuration;
|
|
824
|
+
scale = -Math.abs(scale);
|
|
825
|
+
} else {
|
|
826
|
+
currentTime = currentTime - totalDuration;
|
|
827
|
+
resetCallbacks();
|
|
828
|
+
}
|
|
829
|
+
applyAt(clamp(currentTime, 0, totalDuration), previousTime);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
currentTime = totalDuration;
|
|
833
|
+
applyAt(currentTime, previousTime);
|
|
834
|
+
finish();
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
if (scale < 0 && currentTime <= 0) {
|
|
838
|
+
if (yoyo && (repeat === Infinity || iteration < repeat)) {
|
|
839
|
+
iteration++;
|
|
840
|
+
options.onRepeat?.(iteration);
|
|
841
|
+
currentTime = 0;
|
|
842
|
+
scale = Math.abs(scale);
|
|
843
|
+
applyAt(currentTime, previousTime);
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
currentTime = 0;
|
|
847
|
+
applyAt(currentTime, previousTime);
|
|
848
|
+
finish();
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
applyAt(currentTime, previousTime);
|
|
852
|
+
}
|
|
853
|
+
function resetCallbacks() {
|
|
854
|
+
for (const entry of entries) if (entry.isCallback) entry.fired = false;
|
|
855
|
+
}
|
|
856
|
+
function finish() {
|
|
857
|
+
playing = false;
|
|
858
|
+
subscription?.();
|
|
859
|
+
subscription = null;
|
|
860
|
+
options.onComplete?.();
|
|
861
|
+
resolveFinished();
|
|
862
|
+
}
|
|
863
|
+
const ticker = getTicker();
|
|
864
|
+
const api = {
|
|
865
|
+
to(value, to, sampleOptions = {}, position) {
|
|
866
|
+
const start = resolvePosition(position);
|
|
867
|
+
const sampler = createSampler(value.get(), to, sampleOptions);
|
|
868
|
+
push({
|
|
869
|
+
start,
|
|
870
|
+
duration: sampler.duration,
|
|
871
|
+
apply: (local) => value.set(sampler.at(local)),
|
|
872
|
+
isCallback: false,
|
|
873
|
+
fired: false,
|
|
874
|
+
name: "to"
|
|
875
|
+
});
|
|
876
|
+
return api;
|
|
877
|
+
},
|
|
878
|
+
from(value, from, sampleOptions = {}, position) {
|
|
879
|
+
const start = resolvePosition(position);
|
|
880
|
+
const target = value.get();
|
|
881
|
+
const sampler = createSampler(from, target, sampleOptions);
|
|
882
|
+
push({
|
|
883
|
+
start,
|
|
884
|
+
duration: sampler.duration,
|
|
885
|
+
apply: (local) => value.set(sampler.at(local)),
|
|
886
|
+
isCallback: false,
|
|
887
|
+
fired: false,
|
|
888
|
+
name: "from"
|
|
889
|
+
});
|
|
890
|
+
return api;
|
|
891
|
+
},
|
|
892
|
+
tween(from, to, tweenOptions, position) {
|
|
893
|
+
const start = resolvePosition(position);
|
|
894
|
+
const sampler = createSampler(from, to, tweenOptions);
|
|
895
|
+
const { onUpdate } = tweenOptions;
|
|
896
|
+
push({
|
|
897
|
+
start,
|
|
898
|
+
duration: sampler.duration,
|
|
899
|
+
apply: (local) => onUpdate(sampler.at(local)),
|
|
900
|
+
isCallback: false,
|
|
901
|
+
fired: false,
|
|
902
|
+
name: "tween"
|
|
903
|
+
});
|
|
904
|
+
return api;
|
|
905
|
+
},
|
|
906
|
+
stagger(values, to, staggerOptions = {}, position) {
|
|
907
|
+
const start = resolvePosition(position);
|
|
908
|
+
const delayFor = stagger(values.length, staggerOptions.stagger);
|
|
909
|
+
let groupEnd = start;
|
|
910
|
+
values.forEach((value, index) => {
|
|
911
|
+
const sampler = createSampler(value.get(), to, staggerOptions);
|
|
912
|
+
const entryStart = start + delayFor(index);
|
|
913
|
+
entries.push({
|
|
914
|
+
start: entryStart,
|
|
915
|
+
duration: sampler.duration,
|
|
916
|
+
apply: (local) => value.set(sampler.at(local)),
|
|
917
|
+
isCallback: false,
|
|
918
|
+
fired: false,
|
|
919
|
+
name: `stagger[${index}]`
|
|
920
|
+
});
|
|
921
|
+
const end = entryStart + sampler.duration;
|
|
922
|
+
if (end > groupEnd) groupEnd = end;
|
|
923
|
+
});
|
|
924
|
+
previousStart = start;
|
|
925
|
+
previousEnd = groupEnd;
|
|
926
|
+
if (groupEnd > totalDuration) totalDuration = groupEnd;
|
|
927
|
+
if (autoplay && !playing && !killed) api.play();
|
|
928
|
+
return api;
|
|
929
|
+
},
|
|
930
|
+
call(fn, position) {
|
|
931
|
+
const start = resolvePosition(position);
|
|
932
|
+
push({
|
|
933
|
+
start,
|
|
934
|
+
duration: 0,
|
|
935
|
+
apply: fn,
|
|
936
|
+
isCallback: true,
|
|
937
|
+
fired: false,
|
|
938
|
+
name: "call"
|
|
939
|
+
});
|
|
940
|
+
return api;
|
|
941
|
+
},
|
|
942
|
+
label(name, position) {
|
|
943
|
+
labels.set(name, resolvePosition(position));
|
|
944
|
+
return api;
|
|
945
|
+
},
|
|
946
|
+
add(child, position) {
|
|
947
|
+
const offset = resolvePosition(position);
|
|
948
|
+
for (const entry of child.entries) {
|
|
949
|
+
entries.push({ ...entry, start: entry.start + offset });
|
|
950
|
+
}
|
|
951
|
+
for (const [name, at] of child.labels) {
|
|
952
|
+
labels.set(name, at + offset);
|
|
953
|
+
}
|
|
954
|
+
previousStart = offset;
|
|
955
|
+
previousEnd = offset + child.duration;
|
|
956
|
+
if (previousEnd > totalDuration) totalDuration = previousEnd;
|
|
957
|
+
return api;
|
|
958
|
+
},
|
|
959
|
+
play() {
|
|
960
|
+
if (killed || playing) return api;
|
|
961
|
+
if (respectReducedMotion && prefersReducedMotion()) {
|
|
962
|
+
currentTime = totalDuration;
|
|
963
|
+
applyAt(totalDuration, 0);
|
|
964
|
+
options.onComplete?.();
|
|
965
|
+
resolveFinished();
|
|
966
|
+
return api;
|
|
967
|
+
}
|
|
968
|
+
playing = true;
|
|
969
|
+
subscription = ticker.add(frame, { phase: "compute", name: "timeline" });
|
|
970
|
+
return api;
|
|
971
|
+
},
|
|
972
|
+
pause() {
|
|
973
|
+
playing = false;
|
|
974
|
+
subscription?.();
|
|
975
|
+
subscription = null;
|
|
976
|
+
return api;
|
|
977
|
+
},
|
|
978
|
+
reverse() {
|
|
979
|
+
scale = -Math.abs(scale || 1);
|
|
980
|
+
if (!playing) api.play();
|
|
981
|
+
return api;
|
|
982
|
+
},
|
|
983
|
+
seek(time) {
|
|
984
|
+
const previousTime = currentTime;
|
|
985
|
+
currentTime = clamp(time, 0, totalDuration);
|
|
986
|
+
resetCallbacks();
|
|
987
|
+
applyAt(currentTime, previousTime);
|
|
988
|
+
return api;
|
|
989
|
+
},
|
|
990
|
+
progress(value) {
|
|
991
|
+
return api.seek(clamp(value, 0, 1) * totalDuration);
|
|
992
|
+
},
|
|
993
|
+
restart() {
|
|
994
|
+
iteration = 0;
|
|
995
|
+
scale = Math.abs(scale || 1);
|
|
996
|
+
resetCallbacks();
|
|
997
|
+
api.seek(0);
|
|
998
|
+
return api.play();
|
|
999
|
+
},
|
|
1000
|
+
complete() {
|
|
1001
|
+
api.seek(totalDuration);
|
|
1002
|
+
finish();
|
|
1003
|
+
return api;
|
|
1004
|
+
},
|
|
1005
|
+
kill() {
|
|
1006
|
+
killed = true;
|
|
1007
|
+
playing = false;
|
|
1008
|
+
subscription?.();
|
|
1009
|
+
subscription = null;
|
|
1010
|
+
entries.length = 0;
|
|
1011
|
+
labels.clear();
|
|
1012
|
+
resolveFinished();
|
|
1013
|
+
},
|
|
1014
|
+
get duration() {
|
|
1015
|
+
return totalDuration;
|
|
1016
|
+
},
|
|
1017
|
+
get time() {
|
|
1018
|
+
return currentTime;
|
|
1019
|
+
},
|
|
1020
|
+
get isPlaying() {
|
|
1021
|
+
return playing;
|
|
1022
|
+
},
|
|
1023
|
+
get timeScale() {
|
|
1024
|
+
return scale;
|
|
1025
|
+
},
|
|
1026
|
+
set timeScale(value) {
|
|
1027
|
+
scale = value;
|
|
1028
|
+
},
|
|
1029
|
+
finished,
|
|
1030
|
+
get entries() {
|
|
1031
|
+
return entries;
|
|
1032
|
+
},
|
|
1033
|
+
get labels() {
|
|
1034
|
+
return labels;
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
return api;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// src/observers/scroll.ts
|
|
1041
|
+
var DEFAULT_OFFSET = {
|
|
1042
|
+
start: ["start", "end"],
|
|
1043
|
+
end: ["end", "start"]
|
|
1044
|
+
};
|
|
1045
|
+
function edgeToFraction(edge) {
|
|
1046
|
+
if (typeof edge === "number") return edge;
|
|
1047
|
+
switch (edge) {
|
|
1048
|
+
case "start":
|
|
1049
|
+
return 0;
|
|
1050
|
+
case "center":
|
|
1051
|
+
return 0.5;
|
|
1052
|
+
case "end":
|
|
1053
|
+
return 1;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function trackScroll(element, callback, options = {}) {
|
|
1057
|
+
if (typeof window === "undefined") return () => {
|
|
1058
|
+
};
|
|
1059
|
+
const axis = options.axis ?? "y";
|
|
1060
|
+
const offset = options.offset ?? DEFAULT_OFFSET;
|
|
1061
|
+
const container = options.container ?? null;
|
|
1062
|
+
const state = {
|
|
1063
|
+
progress: 0,
|
|
1064
|
+
velocity: 0,
|
|
1065
|
+
scroll: 0,
|
|
1066
|
+
direction: 0,
|
|
1067
|
+
inView: false
|
|
1068
|
+
};
|
|
1069
|
+
let lastScroll = 0;
|
|
1070
|
+
let hasLastScroll = false;
|
|
1071
|
+
let needsMeasure = true;
|
|
1072
|
+
let startOffset = 0;
|
|
1073
|
+
let endOffset = 0;
|
|
1074
|
+
const readScroll = () => {
|
|
1075
|
+
if (container) return axis === "y" ? container.scrollTop : container.scrollLeft;
|
|
1076
|
+
return axis === "y" ? window.scrollY : window.scrollX;
|
|
1077
|
+
};
|
|
1078
|
+
const viewportSize = () => {
|
|
1079
|
+
if (container) return axis === "y" ? container.clientHeight : container.clientWidth;
|
|
1080
|
+
return axis === "y" ? window.innerHeight : window.innerWidth;
|
|
1081
|
+
};
|
|
1082
|
+
const measure = () => {
|
|
1083
|
+
const rect = element.getBoundingClientRect();
|
|
1084
|
+
const scroll = readScroll();
|
|
1085
|
+
const viewport = viewportSize();
|
|
1086
|
+
const elementStart = (axis === "y" ? rect.top : rect.left) + scroll;
|
|
1087
|
+
const elementSize = axis === "y" ? rect.height : rect.width;
|
|
1088
|
+
const [startElementEdge, startViewportEdge] = offset.start;
|
|
1089
|
+
const [endElementEdge, endViewportEdge] = offset.end;
|
|
1090
|
+
startOffset = elementStart + elementSize * edgeToFraction(startElementEdge) - viewport * edgeToFraction(startViewportEdge);
|
|
1091
|
+
endOffset = elementStart + elementSize * edgeToFraction(endElementEdge) - viewport * edgeToFraction(endViewportEdge);
|
|
1092
|
+
needsMeasure = false;
|
|
1093
|
+
};
|
|
1094
|
+
const invalidate = () => {
|
|
1095
|
+
needsMeasure = true;
|
|
1096
|
+
};
|
|
1097
|
+
window.addEventListener("resize", invalidate, { passive: true });
|
|
1098
|
+
const stopResizeObserver = observeLayoutChanges(element, invalidate);
|
|
1099
|
+
const ticker = getTicker();
|
|
1100
|
+
const stopRead = ticker.add(
|
|
1101
|
+
() => {
|
|
1102
|
+
if (needsMeasure) measure();
|
|
1103
|
+
state.scroll = readScroll();
|
|
1104
|
+
},
|
|
1105
|
+
{ phase: "read", name: "scroll:measure" }
|
|
1106
|
+
);
|
|
1107
|
+
const stopCompute = ticker.add(
|
|
1108
|
+
({ delta }) => {
|
|
1109
|
+
const span = endOffset - startOffset;
|
|
1110
|
+
state.progress = span === 0 ? 0 : clamp((state.scroll - startOffset) / span, 0, 1);
|
|
1111
|
+
if (hasLastScroll && delta > 0) {
|
|
1112
|
+
const dv = (state.scroll - lastScroll) / delta;
|
|
1113
|
+
state.velocity = dv;
|
|
1114
|
+
state.direction = dv > 1 ? 1 : dv < -1 ? -1 : 0;
|
|
1115
|
+
}
|
|
1116
|
+
lastScroll = state.scroll;
|
|
1117
|
+
hasLastScroll = true;
|
|
1118
|
+
state.inView = state.progress > 0 && state.progress < 1;
|
|
1119
|
+
callback(state);
|
|
1120
|
+
},
|
|
1121
|
+
{ phase: "compute", name: "scroll:progress" }
|
|
1122
|
+
);
|
|
1123
|
+
return () => {
|
|
1124
|
+
window.removeEventListener("resize", invalidate);
|
|
1125
|
+
stopResizeObserver();
|
|
1126
|
+
stopRead();
|
|
1127
|
+
stopCompute();
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
function observeLayoutChanges(element, onChange) {
|
|
1131
|
+
return observeResize(element, onChange);
|
|
1132
|
+
}
|
|
1133
|
+
function trackContainerScroll(callback, options = {}) {
|
|
1134
|
+
if (typeof window === "undefined") return () => {
|
|
1135
|
+
};
|
|
1136
|
+
const axis = options.axis ?? "y";
|
|
1137
|
+
const container = options.container ?? null;
|
|
1138
|
+
const ticker = getTicker();
|
|
1139
|
+
let scroll = 0;
|
|
1140
|
+
let max = 1;
|
|
1141
|
+
let lastScroll = 0;
|
|
1142
|
+
let hasLast = false;
|
|
1143
|
+
const stopRead = ticker.add(
|
|
1144
|
+
() => {
|
|
1145
|
+
if (container) {
|
|
1146
|
+
scroll = axis === "y" ? container.scrollTop : container.scrollLeft;
|
|
1147
|
+
max = Math.max(
|
|
1148
|
+
1,
|
|
1149
|
+
axis === "y" ? container.scrollHeight - container.clientHeight : container.scrollWidth - container.clientWidth
|
|
1150
|
+
);
|
|
1151
|
+
} else {
|
|
1152
|
+
scroll = axis === "y" ? window.scrollY : window.scrollX;
|
|
1153
|
+
const doc = document.documentElement;
|
|
1154
|
+
max = Math.max(
|
|
1155
|
+
1,
|
|
1156
|
+
axis === "y" ? doc.scrollHeight - window.innerHeight : doc.scrollWidth - window.innerWidth
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
},
|
|
1160
|
+
{ phase: "read", name: "containerScroll:measure" }
|
|
1161
|
+
);
|
|
1162
|
+
const stopCompute = ticker.add(
|
|
1163
|
+
({ delta }) => {
|
|
1164
|
+
const velocity = hasLast && delta > 0 ? (scroll - lastScroll) / delta : 0;
|
|
1165
|
+
lastScroll = scroll;
|
|
1166
|
+
hasLast = true;
|
|
1167
|
+
callback({ progress: clamp(scroll / max, 0, 1), scroll, velocity, max });
|
|
1168
|
+
},
|
|
1169
|
+
{ phase: "compute", name: "containerScroll:progress" }
|
|
1170
|
+
);
|
|
1171
|
+
return () => {
|
|
1172
|
+
stopRead();
|
|
1173
|
+
stopCompute();
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// src/index.ts
|
|
1178
|
+
var VERSION = "0.1.0";
|
|
1179
|
+
|
|
1180
|
+
export { DEFAULT_TWEEN_DURATION, MotionValue, SPRING_DEFAULTS, VERSION, animate, attachLifecycle, createDecay, createSampler, createSpring, createTimeline, derived, hasSpringOptions, interpolateArray, interpolateColor, interpolateKeyframes, interpolateNumber, interpolateNumericString, interpolateObject, interpolateUnitValue, mapValue, motionValue, perceptualToPhysical, physicalToPerceptual, resolveInterpolator, resolveSpringOptions, springTo, springs, trackContainerScroll, trackScroll };
|
|
1181
|
+
//# sourceMappingURL=index.js.map
|
|
1182
|
+
//# sourceMappingURL=index.js.map
|