@sigx/lynx-motion 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/LICENSE +21 -0
- package/README.md +182 -0
- package/dist/animate.d.ts +58 -0
- package/dist/animate.d.ts.map +1 -0
- package/dist/easings.d.ts +36 -0
- package/dist/easings.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +155 -0
- package/dist/index.js.map +1 -0
- package/dist/spring.d.ts +59 -0
- package/dist/spring.d.ts.map +1 -0
- package/dist/with-spring.d.ts +9 -0
- package/dist/with-spring.d.ts.map +1 -0
- package/dist/with-timing.d.ts +11 -0
- package/dist/with-timing.d.ts.map +1 -0
- package/package.json +51 -0
- package/src/animate.ts +293 -0
- package/src/easings.ts +98 -0
- package/src/index.ts +46 -0
- package/src/spring.ts +198 -0
- package/src/with-spring.ts +18 -0
- package/src/with-timing.ts +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Andreas Ekdahl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# @sigx/lynx-motion
|
|
2
|
+
|
|
3
|
+
Spring and tween animation drivers for [SignalX](https://github.com/signalxjs) on Lynx, built directly on `SharedValue` from [`@sigx/lynx`](../lynx). One customer of the cross-thread bridge alongside gestures and scroll.
|
|
4
|
+
|
|
5
|
+
The differentiator: animation progress is **observable from the background thread** for free. Mutate a `SharedValue` on MT via `withSpring(sv, target)` — the existing diff/publish bridge ships every frame to a BG-side sigx `signal`, so `effect(() => sv.value)` re-runs reactively. Neither `framer-motion` nor `@lynx-js/motion` offers this today; their value primitives are MT-only.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @sigx/lynx-motion
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
The animation tick runs on MT — all `withSpring` / `withTiming` / `animate` calls must sit inside a `'main thread'` context. The simplest path is `main-thread-bindtap` with a `'main thread'` directive on the handler:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { component, useSharedValue, useAnimatedStyle, useMainThreadRef } from '@sigx/lynx';
|
|
19
|
+
import { withSpring, withTiming } from '@sigx/lynx-motion';
|
|
20
|
+
|
|
21
|
+
const App = component(() => {
|
|
22
|
+
const x = useSharedValue(0);
|
|
23
|
+
const boxRef = useMainThreadRef(null);
|
|
24
|
+
useAnimatedStyle(boxRef, x, 'translateX', { factor: 1 });
|
|
25
|
+
|
|
26
|
+
return () => (
|
|
27
|
+
<view>
|
|
28
|
+
<view main-thread:ref={boxRef} style={{ width: 60, height: 60, backgroundColor: '#facc15' }} />
|
|
29
|
+
|
|
30
|
+
<view main-thread-bindtap={() => {
|
|
31
|
+
'main thread';
|
|
32
|
+
withSpring(x, 200, { stiffness: 200, damping: 20 });
|
|
33
|
+
}}>
|
|
34
|
+
<text>spring</text>
|
|
35
|
+
</view>
|
|
36
|
+
|
|
37
|
+
<view main-thread-bindtap={() => {
|
|
38
|
+
'main thread';
|
|
39
|
+
withTiming(x, 0, { duration: 0.4 });
|
|
40
|
+
}}>
|
|
41
|
+
<text>reset (tween)</text>
|
|
42
|
+
</view>
|
|
43
|
+
|
|
44
|
+
{/* BG-reactive — updates per animation frame for free */}
|
|
45
|
+
<text>x = {x.value.toFixed(0)}px</text>
|
|
46
|
+
</view>
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`useAnimatedStyle(elRef, sv, 'translateX')` is required for the bound element to actually move — `withSpring` only writes the SharedValue; the style binding registry (Phase 2.5) is what applies the transform.
|
|
52
|
+
|
|
53
|
+
## API
|
|
54
|
+
|
|
55
|
+
### `animate(sv, target, options?)`
|
|
56
|
+
|
|
57
|
+
Animate a `SharedValue<number>` toward `target`. Returns `{ stop, finished }` controls. **Marked `'main thread'`** — must be called from within a `'main thread'` context.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const ctrl = animate(tx, 200, { type: 'spring', stiffness: 300, damping: 25 });
|
|
61
|
+
// later
|
|
62
|
+
ctrl.stop();
|
|
63
|
+
await ctrl.finished; // resolves on completion or cancel
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`AnimateOptions` extends both `SpringOptions` and `TimingOptions`:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
interface AnimateOptions {
|
|
70
|
+
type?: 'spring' | 'tween'; // default: 'spring' if no duration is set
|
|
71
|
+
// spring physics
|
|
72
|
+
stiffness?: number; // default 100
|
|
73
|
+
damping?: number; // default 10
|
|
74
|
+
mass?: number; // default 1
|
|
75
|
+
velocity?: number; // initial velocity, units/sec, default 0
|
|
76
|
+
restSpeed?: number;
|
|
77
|
+
restDelta?: number;
|
|
78
|
+
// tween
|
|
79
|
+
duration?: number; // seconds, default 0.3
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Default mode is **spring**. Pass `{ type: 'tween' }` or `{ duration }` to get a tween (always uses `easeOut`).
|
|
84
|
+
|
|
85
|
+
### `withSpring(sv, target, options?)` / `withTiming(sv, target, options?)`
|
|
86
|
+
|
|
87
|
+
Promise-returning sugar. Use these when you don't need cancellation — they call `animate()` with the type pinned and return `.finished`.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
await withSpring(tx, 200, { stiffness: 200, damping: 20 });
|
|
91
|
+
await withTiming(tx, 0, { duration: 0.4 });
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Composition
|
|
95
|
+
|
|
96
|
+
Sigx-native idioms cover the patterns motion's `onUpdate` / `onComplete` callbacks would handle in a callback-shaped library — and they integrate cleanly with sigx's reactivity:
|
|
97
|
+
|
|
98
|
+
**Per-frame side effects (replaces `onUpdate`):**
|
|
99
|
+
```tsx
|
|
100
|
+
withSpring(tx, 200);
|
|
101
|
+
// BG side — fires reactively per frame via the SharedValue bridge, zero extra wiring:
|
|
102
|
+
effect(() => updateUI(tx.value));
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Run code on completion (replaces `onComplete`):**
|
|
106
|
+
```tsx
|
|
107
|
+
'main thread';
|
|
108
|
+
await withSpring(sv, 200);
|
|
109
|
+
runOnBackground(() => doNext())();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Concurrent animations:**
|
|
113
|
+
```tsx
|
|
114
|
+
'main thread';
|
|
115
|
+
await Promise.all([
|
|
116
|
+
withSpring(x, 200),
|
|
117
|
+
withSpring(y, 100),
|
|
118
|
+
withTiming(opacity, 1, { duration: 0.3 }),
|
|
119
|
+
]);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Sequential / chained:**
|
|
123
|
+
```tsx
|
|
124
|
+
'main thread';
|
|
125
|
+
await withSpring(x, 200);
|
|
126
|
+
await withTiming(opacity, 0, { duration: 0.2 });
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Mass cancellation (drop down to `animate()` for the controls handle):**
|
|
130
|
+
```tsx
|
|
131
|
+
'main thread';
|
|
132
|
+
const ctrls = [
|
|
133
|
+
animate(a, 100),
|
|
134
|
+
animate(b, 200),
|
|
135
|
+
animate(c, 50),
|
|
136
|
+
];
|
|
137
|
+
// later: stop them all
|
|
138
|
+
ctrls.forEach((c) => c.stop());
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The `__FlushElementTree()` calls each tick performs are coalesced via a microtask flag, so N concurrent animations produce **one flush per frame**, not N. Same pattern upstream's `MTElementWrapper.flushElementTree` uses.
|
|
142
|
+
|
|
143
|
+
### `spring(options)` — solver factory
|
|
144
|
+
|
|
145
|
+
Underneath `animate()`. Exposed for advanced use (driving non-SharedValue values, mocking). Returns a `{ next(elapsedMs): { done, value } }` solver. See `src/spring.ts`.
|
|
146
|
+
|
|
147
|
+
### Easings
|
|
148
|
+
|
|
149
|
+
Built-in: `linear`, `easeIn`, `easeOut`, `easeInOut`, `circIn`, `circOut`, `circInOut`, `backIn`, `backOut`, `backInOut`, `anticipate`. Plus `cubicBezier(x1, y1, x2, y2)` and the `mirrorEasing` / `reverseEasing` modifiers.
|
|
150
|
+
|
|
151
|
+
Tween animations always use the built-in `easeOut`. Custom easing functions can't be passed directly: function references don't survive the worklet `_c` capture across the MT/BG bridge — they'd arrive on MT as `undefined`. If you need a non-built-in curve, either pick a different built-in or wait for `registerEasing(name, fn)` (filed as a follow-up in `NEXT-STEPS.md`).
|
|
152
|
+
|
|
153
|
+
## Cancellation behavior
|
|
154
|
+
|
|
155
|
+
Each `SharedValue` has at most one in-flight animation. Calling `animate()` (or `withSpring`/`withTiming`) on a value that already has an animation in flight **cancels the previous one** before starting the new one. The previous animation's `.finished` promise still resolves (cancellation is not an error).
|
|
156
|
+
|
|
157
|
+
This matches motion's behavior and avoids the race where two ticks fight over the same value.
|
|
158
|
+
|
|
159
|
+
## Tick scheduling
|
|
160
|
+
|
|
161
|
+
Animations tick via `requestAnimationFrame`. Lynx's worklet runtime installs `globalThis.requestAnimationFrame` on MT (Lynx SDK ≥ 2.16). Where rAF isn't available — older SDKs, Node test environments — `@sigx/lynx-motion` falls back to `setTimeout(tick, 16)` (≈60 fps).
|
|
162
|
+
|
|
163
|
+
## Limitations / out of scope
|
|
164
|
+
|
|
165
|
+
- **Scalar `SharedValue<number>` only** for v0.1. 2D values (`{x, y}`) need parallel `animate()` calls (one per axis) for now.
|
|
166
|
+
- **No velocity-carry across animations.** When a new `animate()` cancels an in-flight one, the new one starts at velocity 0. Motion's `MotionValue` tracks velocity to support seamless gesture-to-spring handoff; sigx's `SharedValue` doesn't yet (would require extending `SharedValueState<T>` from `{ value }` to `{ value, velocity }`). Add iff a real use case needs it.
|
|
167
|
+
- **No duration→physics resolution** (motion's `findSpring`). Spring options are physics-only (stiffness/damping/mass). `{ duration, bounce }` not supported. Add iff users want it.
|
|
168
|
+
- **No keyframes / sequences / stagger.** Spring + tween cover gesture-driven UI. The richer orchestration surface (variants, layout, presence, scroll-driven) lives in upstream `@lynx-js/motion` if you need it.
|
|
169
|
+
|
|
170
|
+
## Why we ported instead of using upstream
|
|
171
|
+
|
|
172
|
+
`@lynx-js/motion` ships two entry points: `.` (full, ~200 LOC of thin wrappers around `framer-motion` + `motion-dom`) and `./mini` (lean, ~250 LOC self-contained). Phase 2.6 spiked hosting motion-mini directly on top of sigx via a shim and reverted because the bring-up required eight cumulative pipeline concessions, two with silent-fragility risk (semantic-approximation `useEffect`/`useMemo` shims and an rspack `sideEffects` override of motion's metadata).
|
|
173
|
+
|
|
174
|
+
Porting motion-mini's algorithmic content (spring solver, easings, animate orchestration — ~250 LOC of Apache-2.0 math) was the cleaner path: faithful semantics we own, a sigx-shaped API, and the BG-observable `SharedValue` integration becomes first-class. See `PHASE-2.6-LAYERING-PLAN.md` "What we tried and reverted" for the spike write-up and `PHASE-2.7-MOTION-PLAN.md` "Why mini, not full motion" for the design rationale.
|
|
175
|
+
|
|
176
|
+
## Attribution
|
|
177
|
+
|
|
178
|
+
Spring solver and easing functions are ported from [`@lynx-js/motion`](https://github.com/lynx-family/lynx-stack/tree/main/packages/motion) v0.0.3, [`motion-dom`](https://github.com/motiondivision/motion/tree/main/packages/motion-dom) v12.23.12, and [`motion-utils`](https://github.com/motiondivision/motion/tree/main/packages/motion-utils) v12.23.6 — all Apache-2.0. The cubic bezier code is in turn modified from Gaëtan Renaudeau's [`bezier-easing`](https://github.com/gre/bezier-easing) (MIT). Full attribution in [`THIRD_PARTY_NOTICES.md`](../../THIRD_PARTY_NOTICES.md).
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
MIT (sigx adaptation). Ported portions remain under their upstream licenses (Apache-2.0 and MIT, see attribution above).
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation orchestration ported from `@lynx-js/motion/dist/mini/core/animate.js`
|
|
3
|
+
* v0.0.3 (Apache-2.0). Spring solver + easeOut math from
|
|
4
|
+
* `motion-dom` v12.23.12 + `motion-utils` v12.23.6 (also Apache-2.0),
|
|
5
|
+
* inlined directly inside the worklet body.
|
|
6
|
+
*
|
|
7
|
+
* Why everything is inlined inside the function: SWC's worklet transform
|
|
8
|
+
* captures any free identifier referenced by the worklet body into the
|
|
9
|
+
* `_c` map. Plain JS function references at module scope (or sibling
|
|
10
|
+
* imports) don't survive JSON serialization across the MT/BG bridge — they
|
|
11
|
+
* arrive on MT as undefined or stringified placeholders. Upstream
|
|
12
|
+
* motion-mini sidesteps this by making every helper its own `'main thread'`
|
|
13
|
+
* worklet (so the placeholder objects survive). For Phase 2.7 we keep the
|
|
14
|
+
* port simpler by inlining the math straight into the `animate` worklet
|
|
15
|
+
* body, with `inflight` cancellation state tucked on `globalThis` so it's
|
|
16
|
+
* not a free-identifier capture.
|
|
17
|
+
*
|
|
18
|
+
* Sigx-specific adaptations:
|
|
19
|
+
* - Operates on sigx's `SharedValue<number>` (writes `sv.current.value`)
|
|
20
|
+
* instead of motion's `MotionValue`.
|
|
21
|
+
* - Per-SharedValue cancellation tracking via `globalThis.__sigxMotionInflight`.
|
|
22
|
+
* - Uses `globalThis.requestAnimationFrame` (installed by upstream's
|
|
23
|
+
* worklet-runtime IIFE on MT, Lynx SDK ≥ 2.16); falls back to
|
|
24
|
+
* `setTimeout(cb, 16)` otherwise.
|
|
25
|
+
*
|
|
26
|
+
* The standalone modules `easings.ts` and `spring.ts` still ship as a
|
|
27
|
+
* pure-math API for non-worklet callers (tests, BG-side debugging). They
|
|
28
|
+
* are NOT imported by this worklet.
|
|
29
|
+
*/
|
|
30
|
+
import type { SharedValue } from '@sigx/lynx';
|
|
31
|
+
export interface SpringOptions {
|
|
32
|
+
stiffness?: number;
|
|
33
|
+
damping?: number;
|
|
34
|
+
mass?: number;
|
|
35
|
+
velocity?: number;
|
|
36
|
+
restSpeed?: number;
|
|
37
|
+
restDelta?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface TimingOptions {
|
|
40
|
+
/** Tween duration in seconds. Default 0.3. */
|
|
41
|
+
duration?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface AnimateOptions extends SpringOptions, TimingOptions {
|
|
44
|
+
type?: 'spring' | 'tween';
|
|
45
|
+
}
|
|
46
|
+
export interface AnimateControls {
|
|
47
|
+
stop(): void;
|
|
48
|
+
finished: Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Animate a `SharedValue<number>` toward `target`. Returns
|
|
52
|
+
* `{ stop, finished }`. Default is spring; pass `type: 'tween'` or
|
|
53
|
+
* `{ duration }` to use a tween with `easeOut`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function animate(sv: SharedValue<number>, target: number, options?: AnimateOptions): AnimateControls;
|
|
56
|
+
/** Test hook — clear the in-flight map. Not part of the public API. */
|
|
57
|
+
export declare function _resetInflight(): void;
|
|
58
|
+
//# sourceMappingURL=animate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAe,SAAQ,aAAa,EAAE,aAAa;IAClE,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC3B;AAYD,MAAM,WAAW,eAAe;IAC9B,IAAI,IAAI,IAAI,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAMD;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,EAAE,EAAE,WAAW,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,eAAe,CA4MjB;AAED,uEAAuE;AACvE,wBAAgB,cAAc,IAAI,IAAI,CAGrC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Easing functions ported from `motion-utils` v12.23.6 (Apache-2.0).
|
|
3
|
+
* Upstream: https://github.com/motiondivision/motion/tree/main/packages/motion-utils/src/easing
|
|
4
|
+
*
|
|
5
|
+
* Sigx adaptation:
|
|
6
|
+
* - Inlined into a single file (cubic-bezier + modifiers + named easings).
|
|
7
|
+
* - No `'main thread'` directive; these are pure math functions safe to
|
|
8
|
+
* call from BG or MT context. Sigx's tween path on MT calls them
|
|
9
|
+
* directly inside the worklet body.
|
|
10
|
+
*
|
|
11
|
+
* Reference for individual implementations:
|
|
12
|
+
* - cubicBezier: motion-utils/src/easing/cubic-bezier.ts (modified from
|
|
13
|
+
* Gaëtan Renaudeau's BezierEasing — https://github.com/gre/bezier-easing,
|
|
14
|
+
* MIT-licensed)
|
|
15
|
+
* - ease.ts, back.ts, circ.ts, anticipate.ts, modifiers/{mirror,reverse}.ts
|
|
16
|
+
*
|
|
17
|
+
* Verbatim translation; any divergence from upstream is a bug.
|
|
18
|
+
*/
|
|
19
|
+
export type Easing = (t: number) => number;
|
|
20
|
+
export declare function cubicBezier(mX1: number, mY1: number, mX2: number, mY2: number): Easing;
|
|
21
|
+
/** Reverses an easing — turns easeIn into easeOut. */
|
|
22
|
+
export declare const reverseEasing: (easing: Easing) => Easing;
|
|
23
|
+
/** Mirrors an easing across the midpoint — turns easeIn into easeInOut. */
|
|
24
|
+
export declare const mirrorEasing: (easing: Easing) => Easing;
|
|
25
|
+
export declare const linear: Easing;
|
|
26
|
+
export declare const easeIn: Easing;
|
|
27
|
+
export declare const easeOut: Easing;
|
|
28
|
+
export declare const easeInOut: Easing;
|
|
29
|
+
export declare const circIn: Easing;
|
|
30
|
+
export declare const circOut: Easing;
|
|
31
|
+
export declare const circInOut: Easing;
|
|
32
|
+
export declare const backOut: Easing;
|
|
33
|
+
export declare const backIn: Easing;
|
|
34
|
+
export declare const backInOut: Easing;
|
|
35
|
+
export declare const anticipate: Easing;
|
|
36
|
+
//# sourceMappingURL=easings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"easings.d.ts","sourceRoot":"","sources":["../src/easings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAwC3C,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAIR;AAID,sDAAsD;AACtD,eAAO,MAAM,aAAa,WAAY,MAAM,KAAG,MAC5B,CAAC;AAEpB,2EAA2E;AAC3E,eAAO,MAAM,YAAY,WAAY,MAAM,KAAG,MACgB,CAAC;AAI/D,eAAO,MAAM,MAAM,EAAE,MAAa,CAAC;AAEnC,eAAO,MAAM,MAAM,EAAE,MAAmC,CAAC;AACzD,eAAO,MAAM,OAAO,EAAE,MAAmC,CAAC;AAC1D,eAAO,MAAM,SAAS,EAAE,MAAsC,CAAC;AAE/D,eAAO,MAAM,MAAM,EAAE,MAA0C,CAAC;AAChE,eAAO,MAAM,OAAO,EAAE,MAA8B,CAAC;AACrD,eAAO,MAAM,SAAS,EAAE,MAA6B,CAAC;AAEtD,eAAO,MAAM,OAAO,EAAE,MAA4C,CAAC;AACnE,eAAO,MAAM,MAAM,EAAE,MAA+B,CAAC;AACrD,eAAO,MAAM,SAAS,EAAE,MAA6B,CAAC;AAEtD,eAAO,MAAM,UAAU,EAAE,MACgD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { animate } from './animate.js';
|
|
2
|
+
export type { AnimateOptions, AnimateControls, SpringOptions, TimingOptions, } from './animate.js';
|
|
3
|
+
export { withSpring } from './with-spring.js';
|
|
4
|
+
export { withTiming } from './with-timing.js';
|
|
5
|
+
export { spring, clamp } from './spring.js';
|
|
6
|
+
export type { SpringSolver, SpringStep, SpringSolverOptions, } from './spring.js';
|
|
7
|
+
export { cubicBezier, reverseEasing, mirrorEasing, linear, easeIn, easeOut, easeInOut, circIn, circOut, circInOut, backIn, backOut, backInOut, anticipate, } from './easings.js';
|
|
8
|
+
export type { Easing } from './easings.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,cAAc,EACd,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAO9C,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EACV,YAAY,EACZ,UAAU,EACV,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAMrB,OAAO,EACL,WAAW,EACX,aAAa,EACb,YAAY,EACZ,MAAM,EACN,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,OAAO,EACP,SAAS,EACT,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
//#region src/animate.ts
|
|
2
|
+
function e(e, t, n = {}) {
|
|
3
|
+
"main thread";
|
|
4
|
+
let r = globalThis;
|
|
5
|
+
r.__sigxMotionInflight ||= /* @__PURE__ */ new Map();
|
|
6
|
+
let i = r.__sigxMotionInflight, a = i.get(e._wvid);
|
|
7
|
+
a && a();
|
|
8
|
+
let o = e.current.value, s = n.type === "spring" || n.type !== "tween" && n.duration == null, c = (() => {
|
|
9
|
+
if (!s) return null;
|
|
10
|
+
let e = n.stiffness ?? 100, r = n.damping ?? 10, i = n.mass ?? 1, a = -((n.velocity ?? 0) / 1e3), c = r / (2 * Math.sqrt(e * i)), l = t - o, u = Math.sqrt(e / i) / 1e3, d = Math.abs(l) < 5, f = n.restSpeed ?? (d ? .01 : 2), p = n.restDelta ?? (d ? .005 : .5), m;
|
|
11
|
+
if (c < 1) {
|
|
12
|
+
let e = u * Math.sqrt(1 - c * c);
|
|
13
|
+
m = (n) => t - Math.exp(-c * u * n) * ((a + c * u * l) / e * Math.sin(e * n) + l * Math.cos(e * n));
|
|
14
|
+
} else if (c === 1) m = (e) => t - Math.exp(-u * e) * (l + (a + u * l) * e);
|
|
15
|
+
else {
|
|
16
|
+
let e = u * Math.sqrt(c * c - 1);
|
|
17
|
+
m = (n) => {
|
|
18
|
+
let r = Math.exp(-c * u * n), i = Math.min(e * n, 300);
|
|
19
|
+
return t - r * ((a + c * u * l) * Math.sinh(i) + e * l * Math.cosh(i)) / e;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
let h = (e, t) => {
|
|
23
|
+
let n = Math.max(e - 5, 0), r = m(n), i = e - n;
|
|
24
|
+
return i ? (t - r) * (1e3 / i) : 0;
|
|
25
|
+
};
|
|
26
|
+
return (e) => {
|
|
27
|
+
let n = m(e), r = e === 0 ? a : 0;
|
|
28
|
+
c < 1 && (r = e === 0 ? a * 1e3 : h(e, n));
|
|
29
|
+
let i = Math.abs(r) <= f, o = Math.abs(t - n) <= p, s = i && o;
|
|
30
|
+
return {
|
|
31
|
+
done: s,
|
|
32
|
+
value: s ? t : n
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
})(), l = (e, t, n) => (((1 - 3 * n + 3 * t) * e + (3 * n - 6 * t)) * e + 3 * t) * e, u = (e, t, n) => {
|
|
36
|
+
let r = 0, i = 1, a = 0, o, s = 0;
|
|
37
|
+
do
|
|
38
|
+
a = r + (i - r) / 2, o = l(a, t, n) - e, o > 0 ? i = a : r = a;
|
|
39
|
+
while (Math.abs(o) > 1e-7 && ++s < 12);
|
|
40
|
+
return a;
|
|
41
|
+
}, d = (e) => e === 0 || e === 1 ? e : l(u(e, 0, .58), 0, 1), f = n.duration ?? .3, p = Date.now(), m = !1, h, g = !1, _ = new Promise((e) => {
|
|
42
|
+
h = e;
|
|
43
|
+
}), v = () => {
|
|
44
|
+
g || (g = !0, i.get(e._wvid) === y && i.delete(e._wvid), h?.());
|
|
45
|
+
}, y = () => {
|
|
46
|
+
m || (m = !0, v());
|
|
47
|
+
};
|
|
48
|
+
i.set(e._wvid, y);
|
|
49
|
+
let b = globalThis, x = (e) => {
|
|
50
|
+
typeof b.requestAnimationFrame == "function" ? b.requestAnimationFrame(e) : setTimeout(e, 16);
|
|
51
|
+
}, S = () => {
|
|
52
|
+
let e = globalThis;
|
|
53
|
+
e.__sigxMotionFlushScheduled || (e.__sigxMotionFlushScheduled = !0, Promise.resolve().then(() => {
|
|
54
|
+
e.__sigxMotionFlushScheduled = !1;
|
|
55
|
+
let t = e.__FlushElementTree;
|
|
56
|
+
t && t();
|
|
57
|
+
}));
|
|
58
|
+
}, C = () => {
|
|
59
|
+
if (m) return;
|
|
60
|
+
let n = Date.now() - p, r = n / 1e3, i, a;
|
|
61
|
+
if (c) {
|
|
62
|
+
let e = c(n);
|
|
63
|
+
i = e.value, a = e.done;
|
|
64
|
+
} else if (r >= f) i = t, a = !0;
|
|
65
|
+
else {
|
|
66
|
+
let e = r / f;
|
|
67
|
+
i = o + (t - o) * d(e), a = !1;
|
|
68
|
+
}
|
|
69
|
+
e.current.value = i, S(), a ? (!c && e.current.value !== t && (e.current.value = t, S()), v()) : x(C);
|
|
70
|
+
};
|
|
71
|
+
return x(C), {
|
|
72
|
+
stop: y,
|
|
73
|
+
finished: _
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/with-spring.ts
|
|
78
|
+
function t(t, n, r = {}) {
|
|
79
|
+
"main thread";
|
|
80
|
+
return e(t, n, {
|
|
81
|
+
...r,
|
|
82
|
+
type: "spring"
|
|
83
|
+
}).finished;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/with-timing.ts
|
|
87
|
+
function n(t, n, r = {}) {
|
|
88
|
+
"main thread";
|
|
89
|
+
return e(t, n, {
|
|
90
|
+
...r,
|
|
91
|
+
type: "tween"
|
|
92
|
+
}).finished;
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/spring.ts
|
|
96
|
+
var r = (e, t, n) => n > t ? t : n < e ? e : n, i = (e) => e / 1e3, a = (e) => e * 1e3, o = (e, t) => t ? 1e3 / t * e : 0, s = 5, c = (e, t, n) => {
|
|
97
|
+
let r = Math.max(t - s, 0);
|
|
98
|
+
return o(n - e(r), t - r);
|
|
99
|
+
}, l = {
|
|
100
|
+
stiffness: 100,
|
|
101
|
+
damping: 10,
|
|
102
|
+
mass: 1,
|
|
103
|
+
velocity: 0,
|
|
104
|
+
restSpeed: {
|
|
105
|
+
granular: .01,
|
|
106
|
+
default: 2
|
|
107
|
+
},
|
|
108
|
+
restDelta: {
|
|
109
|
+
granular: .005,
|
|
110
|
+
default: .5
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
function u(e) {
|
|
114
|
+
let t = e.keyframes[0], n = e.keyframes[1], r = {
|
|
115
|
+
done: !1,
|
|
116
|
+
value: t
|
|
117
|
+
}, o = e.stiffness ?? l.stiffness, s = e.damping ?? l.damping, u = e.mass ?? l.mass, d = -i(e.velocity ?? 0), f = s / (2 * Math.sqrt(o * u)), p = n - t, m = i(Math.sqrt(o / u)), h = Math.abs(p) < 5, g = e.restSpeed ?? (h ? l.restSpeed.granular : l.restSpeed.default), _ = e.restDelta ?? (h ? l.restDelta.granular : l.restDelta.default), v;
|
|
118
|
+
if (f < 1) {
|
|
119
|
+
let e = m * Math.sqrt(1 - f * f);
|
|
120
|
+
v = (t) => n - Math.exp(-f * m * t) * ((d + f * m * p) / e * Math.sin(e * t) + p * Math.cos(e * t));
|
|
121
|
+
} else if (f === 1) v = (e) => n - Math.exp(-m * e) * (p + (d + m * p) * e);
|
|
122
|
+
else {
|
|
123
|
+
let e = m * Math.sqrt(f * f - 1);
|
|
124
|
+
v = (t) => {
|
|
125
|
+
let r = Math.exp(-f * m * t), i = Math.min(e * t, 300);
|
|
126
|
+
return n - r * ((d + f * m * p) * Math.sinh(i) + e * p * Math.cosh(i)) / e;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return { next(e) {
|
|
130
|
+
let t = v(e), i = e === 0 ? d : 0;
|
|
131
|
+
f < 1 && (i = e === 0 ? a(d) : c(v, e, t));
|
|
132
|
+
let o = Math.abs(i) <= g, s = Math.abs(n - t) <= _;
|
|
133
|
+
return r.done = o && s, r.value = r.done ? n : t, r;
|
|
134
|
+
} };
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/easings.ts
|
|
138
|
+
var d = (e) => e, f = (e, t, n) => (((1 - 3 * n + 3 * t) * e + (3 * n - 6 * t)) * e + 3 * t) * e, p = 1e-7, m = 12;
|
|
139
|
+
function h(e, t, n, r, i) {
|
|
140
|
+
let a, o = 0, s = 0;
|
|
141
|
+
do
|
|
142
|
+
o = t + (n - t) / 2, a = f(o, r, i) - e, a > 0 ? n = o : t = o;
|
|
143
|
+
while (Math.abs(a) > p && ++s < m);
|
|
144
|
+
return o;
|
|
145
|
+
}
|
|
146
|
+
function g(e, t, n, r) {
|
|
147
|
+
if (e === t && n === r) return d;
|
|
148
|
+
let i = (t) => h(t, 0, 1, e, n);
|
|
149
|
+
return (e) => e === 0 || e === 1 ? e : f(i(e), t, r);
|
|
150
|
+
}
|
|
151
|
+
var _ = (e) => (t) => 1 - e(1 - t), v = (e) => (t) => t <= .5 ? e(2 * t) / 2 : (2 - e(2 * (1 - t))) / 2, y = d, b = g(.42, 0, 1, 1), x = g(0, 0, .58, 1), S = g(.42, 0, .58, 1), C = (e) => 1 - Math.sin(Math.acos(e)), w = _(C), T = v(C), E = g(.33, 1.53, .69, .99), D = _(E), O = v(D), k = (e) => (e *= 2) < 1 ? .5 * D(e) : .5 * (2 - 2 ** (-10 * (e - 1)));
|
|
152
|
+
//#endregion
|
|
153
|
+
export { e as animate, k as anticipate, D as backIn, O as backInOut, E as backOut, C as circIn, T as circInOut, w as circOut, r as clamp, g as cubicBezier, b as easeIn, S as easeInOut, x as easeOut, y as linear, v as mirrorEasing, _ as reverseEasing, u as spring, t as withSpring, n as withTiming };
|
|
154
|
+
|
|
155
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/animate.ts","../src/with-spring.ts","../src/with-timing.ts","../src/spring.ts","../src/easings.ts"],"sourcesContent":["/**\n * Animation orchestration ported from `@lynx-js/motion/dist/mini/core/animate.js`\n * v0.0.3 (Apache-2.0). Spring solver + easeOut math from\n * `motion-dom` v12.23.12 + `motion-utils` v12.23.6 (also Apache-2.0),\n * inlined directly inside the worklet body.\n *\n * Why everything is inlined inside the function: SWC's worklet transform\n * captures any free identifier referenced by the worklet body into the\n * `_c` map. Plain JS function references at module scope (or sibling\n * imports) don't survive JSON serialization across the MT/BG bridge — they\n * arrive on MT as undefined or stringified placeholders. Upstream\n * motion-mini sidesteps this by making every helper its own `'main thread'`\n * worklet (so the placeholder objects survive). For Phase 2.7 we keep the\n * port simpler by inlining the math straight into the `animate` worklet\n * body, with `inflight` cancellation state tucked on `globalThis` so it's\n * not a free-identifier capture.\n *\n * Sigx-specific adaptations:\n * - Operates on sigx's `SharedValue<number>` (writes `sv.current.value`)\n * instead of motion's `MotionValue`.\n * - Per-SharedValue cancellation tracking via `globalThis.__sigxMotionInflight`.\n * - Uses `globalThis.requestAnimationFrame` (installed by upstream's\n * worklet-runtime IIFE on MT, Lynx SDK ≥ 2.16); falls back to\n * `setTimeout(cb, 16)` otherwise.\n *\n * The standalone modules `easings.ts` and `spring.ts` still ship as a\n * pure-math API for non-worklet callers (tests, BG-side debugging). They\n * are NOT imported by this worklet.\n */\n\nimport type { SharedValue } from '@sigx/lynx';\n\n// ============================================================================\n// Public types\n// ============================================================================\n\nexport interface SpringOptions {\n stiffness?: number;\n damping?: number;\n mass?: number;\n velocity?: number;\n restSpeed?: number;\n restDelta?: number;\n}\n\nexport interface TimingOptions {\n /** Tween duration in seconds. Default 0.3. */\n duration?: number;\n}\n\nexport interface AnimateOptions extends SpringOptions, TimingOptions {\n type?: 'spring' | 'tween';\n}\n\n// Note: motion-style `onUpdate` / `onComplete` / custom `ease` callbacks are\n// intentionally NOT in this surface. Function references don't survive\n// worklet `_c` capture across the MT/BG bridge — they'd be silent footguns.\n// Sigx-native equivalents (zero extra wiring, integrate with reactivity):\n// onUpdate(v) → BG: `effect(() => doX(sv.value))`\n// onComplete() → `await withSpring(sv, target); doNext()`\n// ease: customFn → use a built-in (`linear`, `easeIn`, `easeOut`, etc.);\n// custom curves are rare and would need a `registerEasing(name, fn)`\n// pattern (deferred follow-up).\n\nexport interface AnimateControls {\n stop(): void;\n finished: Promise<void>;\n}\n\n// ============================================================================\n// Public API — all math inlined inside the worklet body.\n// ============================================================================\n\n/**\n * Animate a `SharedValue<number>` toward `target`. Returns\n * `{ stop, finished }`. Default is spring; pass `type: 'tween'` or\n * `{ duration }` to use a tween with `easeOut`.\n */\nexport function animate(\n sv: SharedValue<number>,\n target: number,\n options: AnimateOptions = {},\n): AnimateControls {\n 'main thread';\n\n // ─── Inlined: in-flight cancellation map (per-SharedValue) ──────────\n // Stash on globalThis so SWC doesn't capture a module-scope reference\n // into _c. The map persists across animate() calls on MT.\n const g = globalThis as { __sigxMotionInflight?: Map<number, () => void> };\n if (!g.__sigxMotionInflight) g.__sigxMotionInflight = new Map();\n const inflight = g.__sigxMotionInflight;\n\n const prev = inflight.get(sv._wvid);\n if (prev) prev();\n\n const startValue = sv.current.value;\n\n const isSpring =\n options.type === 'spring' ||\n (options.type !== 'tween' && options.duration == null);\n\n // ─── Inlined: spring solver (motion-dom port, Apache-2.0) ────────────\n // Returns { next(elapsedMs): { done, value } }. Only built when isSpring.\n const buildSolver = (): ((t: number) => { done: boolean; value: number }) | null => {\n if (!isSpring) return null;\n\n const stiffness = options.stiffness ?? 100;\n const damping = options.damping ?? 10;\n const mass = options.mass ?? 1;\n const initialVelocity = -((options.velocity ?? 0) / 1000); // ms→s, sign convention\n\n const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));\n const initialDelta = target - startValue;\n const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;\n\n const isGranularScale = Math.abs(initialDelta) < 5;\n const restSpeed = options.restSpeed ?? (isGranularScale ? 0.01 : 2);\n const restDelta = options.restDelta ?? (isGranularScale ? 0.005 : 0.5);\n\n let resolveSpring: (t: number) => number;\n\n if (dampingRatio < 1) {\n const angularFreq = undampedAngularFreq * Math.sqrt(1 - dampingRatio * dampingRatio);\n resolveSpring = (t) => {\n const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);\n return target - envelope *\n (((initialVelocity + dampingRatio * undampedAngularFreq * initialDelta) / angularFreq) *\n Math.sin(angularFreq * t) +\n initialDelta * Math.cos(angularFreq * t));\n };\n } else if (dampingRatio === 1) {\n resolveSpring = (t) =>\n target - Math.exp(-undampedAngularFreq * t) *\n (initialDelta + (initialVelocity + undampedAngularFreq * initialDelta) * t);\n } else {\n const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);\n resolveSpring = (t) => {\n const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);\n const freqForT = Math.min(dampedAngularFreq * t, 300);\n return target - (envelope *\n ((initialVelocity + dampingRatio * undampedAngularFreq * initialDelta) * Math.sinh(freqForT) +\n dampedAngularFreq * initialDelta * Math.cosh(freqForT))) / dampedAngularFreq;\n };\n }\n\n // Velocity sample: 5ms-window finite-difference of resolveSpring.\n const calcVelocity = (t: number, current: number): number => {\n const prevT = Math.max(t - 5, 0);\n const prevVal = resolveSpring(prevT);\n const dt = t - prevT;\n return dt ? (current - prevVal) * (1000 / dt) : 0;\n };\n\n return (t: number) => {\n const current = resolveSpring(t);\n let currentVelocity = t === 0 ? initialVelocity : 0;\n if (dampingRatio < 1) {\n currentVelocity = t === 0 ? initialVelocity * 1000 : calcVelocity(t, current);\n }\n const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;\n const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;\n const done = isBelowVelocityThreshold && isBelowDisplacementThreshold;\n return { done, value: done ? target : current };\n };\n };\n\n const solverNext = buildSolver();\n\n // ─── Inlined: easeOut (cubicBezier(0, 0, 0.58, 1)) ────────────────────\n const calcBezier = (t: number, a1: number, a2: number): number =>\n (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) * t;\n\n const binarySubdivide = (x: number, mX1: number, mX2: number): number => {\n let lo = 0, hi = 1, cT = 0, cX: number, i = 0;\n do {\n cT = lo + (hi - lo) / 2;\n cX = calcBezier(cT, mX1, mX2) - x;\n if (cX > 0) hi = cT; else lo = cT;\n } while (Math.abs(cX) > 1e-7 && ++i < 12);\n return cT;\n };\n\n const easeOutDefault = (t: number): number => {\n if (t === 0 || t === 1) return t;\n return calcBezier(binarySubdivide(t, 0, 0.58), 0, 1);\n };\n\n const duration = options.duration ?? 0.3;\n const startTime = Date.now();\n\n let canceled = false;\n let resolvePromise: (() => void) | undefined;\n let settled = false;\n const finished = new Promise<void>((resolve) => { resolvePromise = resolve; });\n\n const settle = (): void => {\n if (settled) return;\n settled = true;\n if (inflight.get(sv._wvid) === stop) inflight.delete(sv._wvid);\n resolvePromise?.();\n };\n\n const stop = (): void => {\n if (canceled) return;\n canceled = true;\n settle();\n };\n\n inflight.set(sv._wvid, stop);\n\n // ─── Inlined: tick scheduler ──────────────────────────────────────────\n // `globalThis.requestAnimationFrame` is installed by upstream's\n // worklet-runtime IIFE on MT; falls back to `setTimeout(cb, 16)` when\n // unavailable (older SDKs, test environments).\n const rafGlobal = globalThis as { requestAnimationFrame?: (cb: () => void) => void };\n const scheduleNext = (cb: () => void): void => {\n if (typeof rafGlobal.requestAnimationFrame === 'function') {\n rafGlobal.requestAnimationFrame(cb);\n } else {\n setTimeout(cb, 16);\n }\n };\n\n // ─── Flush trigger (microtask-debounced) ─────────────────────────────\n // Writing `sv.current.value` is a plain ref mutation — it doesn't\n // schedule the native flush by itself. Without a flush, the SharedValue\n // bridge never publishes the new value to BG and `useAnimatedStyle`\n // bindings never apply.\n //\n // We coalesce calls via a `globalThis.__sigxMotionFlushScheduled` flag:\n // multiple ticks across multiple concurrent animations within the same\n // microtask boundary all set the flag, the first one schedules the\n // microtask, the rest see the flag and bail. End result: ONE flush per\n // microtask regardless of how many animations are live. Same pattern\n // upstream's `MTElementWrapper.flushElementTree` uses.\n const flushTree = (): void => {\n const g = globalThis as Record<string, unknown>;\n if (g['__sigxMotionFlushScheduled']) return;\n g['__sigxMotionFlushScheduled'] = true;\n Promise.resolve().then(() => {\n g['__sigxMotionFlushScheduled'] = false;\n const fn = g['__FlushElementTree'] as (() => void) | undefined;\n if (fn) fn();\n });\n };\n\n const tick = (): void => {\n if (canceled) return;\n\n const now = Date.now();\n const elapsedMs = now - startTime;\n const elapsedSec = elapsedMs / 1000;\n\n let current: number;\n let done: boolean;\n\n if (solverNext) {\n const state = solverNext(elapsedMs);\n current = state.value;\n done = state.done;\n } else if (elapsedSec >= duration) {\n current = target;\n done = true;\n } else {\n const p = elapsedSec / duration;\n current = startValue + (target - startValue) * easeOutDefault(p);\n done = false;\n }\n\n sv.current.value = current;\n flushTree();\n\n if (done) {\n if (!solverNext && sv.current.value !== target) {\n sv.current.value = target;\n flushTree();\n }\n settle();\n } else {\n scheduleNext(tick);\n }\n };\n\n scheduleNext(tick);\n\n return { stop, finished };\n}\n\n/** Test hook — clear the in-flight map. Not part of the public API. */\nexport function _resetInflight(): void {\n const g = globalThis as { __sigxMotionInflight?: Map<number, () => void> };\n if (g.__sigxMotionInflight) g.__sigxMotionInflight.clear();\n}\n","/**\n * Convenience wrapper: animate(sv, target, { type: 'spring', ...opts }).\n * Returns the completion promise rather than the full controls handle —\n * use `animate()` directly if you need cancellation.\n */\n\nimport type { SharedValue } from '@sigx/lynx';\n\nimport { animate, type SpringOptions } from './animate.js';\n\nexport function withSpring(\n sv: SharedValue<number>,\n target: number,\n options: SpringOptions = {},\n): Promise<void> {\n 'main thread';\n return animate(sv, target, { ...options, type: 'spring' }).finished;\n}\n","/**\n * Convenience wrapper: animate(sv, target, { type: 'tween', ...opts }).\n * Returns the completion promise rather than the full controls handle —\n * use `animate()` directly if you need cancellation.\n *\n * Duration is in seconds (default 0.3). Ease defaults to `easeOut`.\n */\n\nimport type { SharedValue } from '@sigx/lynx';\n\nimport { animate, type TimingOptions } from './animate.js';\n\nexport function withTiming(\n sv: SharedValue<number>,\n target: number,\n options: TimingOptions = {},\n): Promise<void> {\n 'main thread';\n return animate(sv, target, { ...options, type: 'tween' }).finished;\n}\n","/**\n * Spring solver ported from `motion-dom` v12.23.12 + `motion-utils` v12.23.6\n * (both Apache-2.0).\n * Upstream:\n * - https://github.com/motiondivision/motion/tree/main/packages/motion-dom/src/animation/generators/spring\n * - https://github.com/motiondivision/motion/tree/main/packages/motion-utils/src\n *\n * Sigx adaptation:\n * - Inlined the motion-utils helpers we depend on (`clamp`,\n * `millisecondsToSeconds`, `secondsToMilliseconds`, `velocityPerSecond`)\n * and the velocity calculator from `motion-dom/animation/generators/utils`.\n * These are tiny pure functions; inlining keeps `@sigx/lynx-motion` zero-deps\n * beyond the sigx workspace.\n * - Skipped the duration→physics resolution path (motion's `findSpring`).\n * Phase 2.7 ships only physics-based options (stiffness/damping/mass).\n * If a future user wants `withSpring(av, target, { duration, bounce })`,\n * add `findSpring` then.\n * - `calculatedDuration` and `toString()` / `toTransition()` from upstream\n * are dropped; they're for WAAPI integration which Lynx doesn't use.\n *\n * Verbatim translation of the integrator math; any divergence is a bug.\n */\n\n// ---- inlined helpers ------------------------------------------------------\n\nconst clamp = (min: number, max: number, v: number): number =>\n v > max ? max : v < min ? min : v;\n\nconst millisecondsToSeconds = (ms: number): number => ms / 1000;\nconst secondsToMilliseconds = (s: number): number => s * 1000;\n\nconst velocityPerSecond = (velocity: number, frameDuration: number): number =>\n frameDuration ? velocity * (1000 / frameDuration) : 0;\n\nconst velocitySampleDuration = 5; // ms\nconst calcGeneratorVelocity = (\n resolveValue: (t: number) => number,\n t: number,\n current: number,\n): number => {\n const prevT = Math.max(t - velocitySampleDuration, 0);\n return velocityPerSecond(current - resolveValue(prevT), t - prevT);\n};\n\n// ---- defaults -------------------------------------------------------------\n\nconst springDefaults = {\n stiffness: 100,\n damping: 10,\n mass: 1.0,\n velocity: 0.0,\n restSpeed: { granular: 0.01, default: 2 },\n restDelta: { granular: 0.005, default: 0.5 },\n};\n\n// ---- public API -----------------------------------------------------------\n\nexport interface SpringOptions {\n /** Spring physics. Default 100. */\n stiffness?: number;\n /** Spring damping. Default 10. */\n damping?: number;\n /** Mass of the spring object. Default 1. */\n mass?: number;\n /** Initial velocity in units/sec. Default 0. */\n velocity?: number;\n /** Threshold below which the spring is considered at rest. */\n restSpeed?: number;\n restDelta?: number;\n}\n\nexport interface SpringStep {\n done: boolean;\n value: number;\n}\n\nexport interface SpringSolver {\n /**\n * Advance the spring to `t` ms after `t=0`. Returns the integrated value\n * and a `done` flag set when the spring is below the rest thresholds.\n */\n next(t: number): SpringStep;\n}\n\nexport interface SpringSolverOptions extends SpringOptions {\n /** Required: `[origin, target]`. Pinned to a 2-element keyframes shape. */\n keyframes: [number, number];\n}\n\n/**\n * Build a spring solver. Call `.next(elapsedMs)` repeatedly to step the\n * animation. The solver owns no time state — call `.next` with the current\n * elapsed time each tick (motion's pattern).\n */\nexport function spring(options: SpringSolverOptions): SpringSolver {\n const origin = options.keyframes[0];\n const target = options.keyframes[1];\n\n const state: SpringStep = { done: false, value: origin };\n\n const stiffness = options.stiffness ?? springDefaults.stiffness;\n const damping = options.damping ?? springDefaults.damping;\n const mass = options.mass ?? springDefaults.mass;\n // Negate to match motion's convention: positive velocity moves toward\n // target, but the integrator expects velocity in source-units convention.\n const initialVelocity = -millisecondsToSeconds(options.velocity ?? 0);\n\n const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));\n const initialDelta = target - origin;\n const undampedAngularFreq = millisecondsToSeconds(\n Math.sqrt(stiffness / mass),\n );\n\n const isGranularScale = Math.abs(initialDelta) < 5;\n const restSpeed =\n options.restSpeed ??\n (isGranularScale\n ? springDefaults.restSpeed.granular\n : springDefaults.restSpeed.default);\n const restDelta =\n options.restDelta ??\n (isGranularScale\n ? springDefaults.restDelta.granular\n : springDefaults.restDelta.default);\n\n let resolveSpring: (t: number) => number;\n\n if (dampingRatio < 1) {\n // Underdamped — oscillates while decaying.\n const angularFreq =\n undampedAngularFreq * Math.sqrt(1 - dampingRatio * dampingRatio);\n resolveSpring = (t) => {\n const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);\n return (\n target -\n envelope *\n (((initialVelocity +\n dampingRatio * undampedAngularFreq * initialDelta) /\n angularFreq) *\n Math.sin(angularFreq * t) +\n initialDelta * Math.cos(angularFreq * t))\n );\n };\n } else if (dampingRatio === 1) {\n // Critically damped — fastest non-oscillating return.\n resolveSpring = (t) =>\n target -\n Math.exp(-undampedAngularFreq * t) *\n (initialDelta +\n (initialVelocity + undampedAngularFreq * initialDelta) * t);\n } else {\n // Overdamped — slow non-oscillating return.\n const dampedAngularFreq =\n undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);\n resolveSpring = (t) => {\n const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);\n // Cap freq*t to keep sinh/cosh from hitting Infinity.\n const freqForT = Math.min(dampedAngularFreq * t, 300);\n return (\n target -\n (envelope *\n ((initialVelocity +\n dampingRatio * undampedAngularFreq * initialDelta) *\n Math.sinh(freqForT) +\n dampedAngularFreq * initialDelta * Math.cosh(freqForT))) /\n dampedAngularFreq\n );\n };\n }\n\n return {\n next(t: number): SpringStep {\n const current = resolveSpring(t);\n\n let currentVelocity = t === 0 ? initialVelocity : 0.0;\n // Velocity calc only needed for underdamped — over/critically-damped\n // can't overshoot, so position alone tells us we're done.\n if (dampingRatio < 1) {\n currentVelocity =\n t === 0\n ? secondsToMilliseconds(initialVelocity)\n : calcGeneratorVelocity(resolveSpring, t, current);\n }\n\n const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;\n const isBelowDisplacementThreshold =\n Math.abs(target - current) <= restDelta;\n\n state.done = isBelowVelocityThreshold && isBelowDisplacementThreshold;\n state.value = state.done ? target : current;\n return state;\n },\n };\n}\n\n// Re-export `clamp` because it's small and useful for animation math, and\n// motion-mini exports it from its mini surface.\nexport { clamp };\n","/**\n * Easing functions ported from `motion-utils` v12.23.6 (Apache-2.0).\n * Upstream: https://github.com/motiondivision/motion/tree/main/packages/motion-utils/src/easing\n *\n * Sigx adaptation:\n * - Inlined into a single file (cubic-bezier + modifiers + named easings).\n * - No `'main thread'` directive; these are pure math functions safe to\n * call from BG or MT context. Sigx's tween path on MT calls them\n * directly inside the worklet body.\n *\n * Reference for individual implementations:\n * - cubicBezier: motion-utils/src/easing/cubic-bezier.ts (modified from\n * Gaëtan Renaudeau's BezierEasing — https://github.com/gre/bezier-easing,\n * MIT-licensed)\n * - ease.ts, back.ts, circ.ts, anticipate.ts, modifiers/{mirror,reverse}.ts\n *\n * Verbatim translation; any divergence from upstream is a bug.\n */\n\nexport type Easing = (t: number) => number;\n\n// noop — identity; serves as `linear` and as the cubic-bezier shortcut when\n// (mX1, mY1) === (mX2, mY2).\nconst noop: Easing = (t) => t;\n\n// ---- cubic bezier ---------------------------------------------------------\n\nconst calcBezier = (t: number, a1: number, a2: number): number =>\n (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *\n t;\n\nconst subdivisionPrecision = 0.0000001;\nconst subdivisionMaxIterations = 12;\n\nfunction binarySubdivide(\n x: number,\n lowerBound: number,\n upperBound: number,\n mX1: number,\n mX2: number,\n): number {\n let currentX: number;\n let currentT: number = 0;\n let i = 0;\n do {\n currentT = lowerBound + (upperBound - lowerBound) / 2.0;\n currentX = calcBezier(currentT, mX1, mX2) - x;\n if (currentX > 0.0) {\n upperBound = currentT;\n } else {\n lowerBound = currentT;\n }\n } while (\n Math.abs(currentX) > subdivisionPrecision &&\n ++i < subdivisionMaxIterations\n );\n return currentT;\n}\n\nexport function cubicBezier(\n mX1: number,\n mY1: number,\n mX2: number,\n mY2: number,\n): Easing {\n if (mX1 === mY1 && mX2 === mY2) return noop;\n const getTForX = (aX: number) => binarySubdivide(aX, 0, 1, mX1, mX2);\n return (t) => (t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2));\n}\n\n// ---- modifiers ------------------------------------------------------------\n\n/** Reverses an easing — turns easeIn into easeOut. */\nexport const reverseEasing = (easing: Easing): Easing => (p) =>\n 1 - easing(1 - p);\n\n/** Mirrors an easing across the midpoint — turns easeIn into easeInOut. */\nexport const mirrorEasing = (easing: Easing): Easing => (p) =>\n p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;\n\n// ---- named easings --------------------------------------------------------\n\nexport const linear: Easing = noop;\n\nexport const easeIn: Easing = cubicBezier(0.42, 0, 1, 1);\nexport const easeOut: Easing = cubicBezier(0, 0, 0.58, 1);\nexport const easeInOut: Easing = cubicBezier(0.42, 0, 0.58, 1);\n\nexport const circIn: Easing = (p) => 1 - Math.sin(Math.acos(p));\nexport const circOut: Easing = reverseEasing(circIn);\nexport const circInOut: Easing = mirrorEasing(circIn);\n\nexport const backOut: Easing = cubicBezier(0.33, 1.53, 0.69, 0.99);\nexport const backIn: Easing = reverseEasing(backOut);\nexport const backInOut: Easing = mirrorEasing(backIn);\n\nexport const anticipate: Easing = (p) =>\n (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));\n"],"mappings":"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"}
|
package/dist/spring.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spring solver ported from `motion-dom` v12.23.12 + `motion-utils` v12.23.6
|
|
3
|
+
* (both Apache-2.0).
|
|
4
|
+
* Upstream:
|
|
5
|
+
* - https://github.com/motiondivision/motion/tree/main/packages/motion-dom/src/animation/generators/spring
|
|
6
|
+
* - https://github.com/motiondivision/motion/tree/main/packages/motion-utils/src
|
|
7
|
+
*
|
|
8
|
+
* Sigx adaptation:
|
|
9
|
+
* - Inlined the motion-utils helpers we depend on (`clamp`,
|
|
10
|
+
* `millisecondsToSeconds`, `secondsToMilliseconds`, `velocityPerSecond`)
|
|
11
|
+
* and the velocity calculator from `motion-dom/animation/generators/utils`.
|
|
12
|
+
* These are tiny pure functions; inlining keeps `@sigx/lynx-motion` zero-deps
|
|
13
|
+
* beyond the sigx workspace.
|
|
14
|
+
* - Skipped the duration→physics resolution path (motion's `findSpring`).
|
|
15
|
+
* Phase 2.7 ships only physics-based options (stiffness/damping/mass).
|
|
16
|
+
* If a future user wants `withSpring(av, target, { duration, bounce })`,
|
|
17
|
+
* add `findSpring` then.
|
|
18
|
+
* - `calculatedDuration` and `toString()` / `toTransition()` from upstream
|
|
19
|
+
* are dropped; they're for WAAPI integration which Lynx doesn't use.
|
|
20
|
+
*
|
|
21
|
+
* Verbatim translation of the integrator math; any divergence is a bug.
|
|
22
|
+
*/
|
|
23
|
+
declare const clamp: (min: number, max: number, v: number) => number;
|
|
24
|
+
export interface SpringOptions {
|
|
25
|
+
/** Spring physics. Default 100. */
|
|
26
|
+
stiffness?: number;
|
|
27
|
+
/** Spring damping. Default 10. */
|
|
28
|
+
damping?: number;
|
|
29
|
+
/** Mass of the spring object. Default 1. */
|
|
30
|
+
mass?: number;
|
|
31
|
+
/** Initial velocity in units/sec. Default 0. */
|
|
32
|
+
velocity?: number;
|
|
33
|
+
/** Threshold below which the spring is considered at rest. */
|
|
34
|
+
restSpeed?: number;
|
|
35
|
+
restDelta?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface SpringStep {
|
|
38
|
+
done: boolean;
|
|
39
|
+
value: number;
|
|
40
|
+
}
|
|
41
|
+
export interface SpringSolver {
|
|
42
|
+
/**
|
|
43
|
+
* Advance the spring to `t` ms after `t=0`. Returns the integrated value
|
|
44
|
+
* and a `done` flag set when the spring is below the rest thresholds.
|
|
45
|
+
*/
|
|
46
|
+
next(t: number): SpringStep;
|
|
47
|
+
}
|
|
48
|
+
export interface SpringSolverOptions extends SpringOptions {
|
|
49
|
+
/** Required: `[origin, target]`. Pinned to a 2-element keyframes shape. */
|
|
50
|
+
keyframes: [number, number];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build a spring solver. Call `.next(elapsedMs)` repeatedly to step the
|
|
54
|
+
* animation. The solver owns no time state — call `.next` with the current
|
|
55
|
+
* elapsed time each tick (motion's pattern).
|
|
56
|
+
*/
|
|
57
|
+
export declare function spring(options: SpringSolverOptions): SpringSolver;
|
|
58
|
+
export { clamp };
|
|
59
|
+
//# sourceMappingURL=spring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spring.d.ts","sourceRoot":"","sources":["../src/spring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,QAAA,MAAM,KAAK,QAAS,MAAM,OAAO,MAAM,KAAK,MAAM,KAAG,MAClB,CAAC;AA+BpC,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,2EAA2E;IAC3E,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAmGjE;AAID,OAAO,EAAE,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convenience wrapper: animate(sv, target, { type: 'spring', ...opts }).
|
|
3
|
+
* Returns the completion promise rather than the full controls handle —
|
|
4
|
+
* use `animate()` directly if you need cancellation.
|
|
5
|
+
*/
|
|
6
|
+
import type { SharedValue } from '@sigx/lynx';
|
|
7
|
+
import { type SpringOptions } from './animate.js';
|
|
8
|
+
export declare function withSpring(sv: SharedValue<number>, target: number, options?: SpringOptions): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=with-spring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-spring.d.ts","sourceRoot":"","sources":["../src/with-spring.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAE3D,wBAAgB,UAAU,CACxB,EAAE,EAAE,WAAW,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convenience wrapper: animate(sv, target, { type: 'tween', ...opts }).
|
|
3
|
+
* Returns the completion promise rather than the full controls handle —
|
|
4
|
+
* use `animate()` directly if you need cancellation.
|
|
5
|
+
*
|
|
6
|
+
* Duration is in seconds (default 0.3). Ease defaults to `easeOut`.
|
|
7
|
+
*/
|
|
8
|
+
import type { SharedValue } from '@sigx/lynx';
|
|
9
|
+
import { type TimingOptions } from './animate.js';
|
|
10
|
+
export declare function withTiming(sv: SharedValue<number>, target: number, options?: TimingOptions): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=with-timing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-timing.d.ts","sourceRoot":"","sources":["../src/with-timing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAE3D,wBAAgB,UAAU,CACxB,EAAE,EAAE,WAAW,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAGf"}
|