coily 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -40
- package/dist/config-OOMMRdK8.d.ts +312 -0
- package/dist/css.d.ts +175 -0
- package/dist/css.js +294 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +91 -2
- package/dist/nuxt.d.ts +46 -0
- package/dist/nuxt.js +14 -14
- package/dist/solver-CVXOTDkZ.js +235 -0
- package/dist/system-Br_cPnaN.js +2188 -0
- package/dist/system-Du_fXXmO.d.ts +940 -0
- package/dist/vue.d.ts +227 -16
- package/dist/vue.js +84 -42
- package/package.json +2 -1
- package/dist/system-86fsXJN_.d.ts +0 -386
- package/dist/system-Csho09XW.js +0 -1384
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Simulate values using spring physics.
|
|
4
4
|
|
|
5
|
-
Coily animates numbers
|
|
5
|
+
Coily animates numbers and whole numeric shapes with damped spring motion. It computes each frame from the closed-form solution of the spring equation (underdamped, critically damped, or overdamped) rather than numerical integration, so motion doesn't accumulate error and you can retarget a spring mid-flight without losing momentum.
|
|
6
6
|
|
|
7
|
-
The core has zero dependencies and runs anywhere (it falls back from `requestAnimationFrame` to `setTimeout` outside the browser). Vue and Nuxt integrations ship as separate entry points
|
|
7
|
+
The core has zero dependencies and runs anywhere (it falls back from `requestAnimationFrame` to `setTimeout` outside the browser). Vue and Nuxt integrations ship as separate entry points, with `vue` and `@nuxt/kit` as optional peer dependencies.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -33,7 +33,7 @@ spring.target = 300
|
|
|
33
33
|
|
|
34
34
|
## Configuring springs
|
|
35
35
|
|
|
36
|
-
`defineSpring` accepts several input shapes
|
|
36
|
+
`defineSpring` accepts several input shapes and derives whatever you leave out:
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
39
|
defineSpring({ tension: 500, damping: 40 }) // direct physical parameters
|
|
@@ -41,81 +41,115 @@ defineSpring({ tension: 500, dampingRatio: 0.7 }) // damping derived
|
|
|
41
41
|
defineSpring({ tension: 500, bounce: 0.3 }) // bounce = 1 - dampingRatio
|
|
42
42
|
defineSpring({ duration: 750, dampingRatio: 1 }) // tuned to settle in ~750ms
|
|
43
43
|
defineSpring({ duration: 750, bounce: 0.5 })
|
|
44
|
+
defineSpring({ tension: 500, bounce: 0.9, arrival: 'stop' }) // dead-stop at the target, no overshoot
|
|
44
45
|
```
|
|
45
46
|
|
|
46
|
-
- **`tension
|
|
47
|
-
- **`damping
|
|
48
|
-
- **`dampingRatio
|
|
49
|
-
- **`bounce
|
|
50
|
-
- **`duration
|
|
51
|
-
- **`mass
|
|
52
|
-
- **`precision
|
|
47
|
+
- **`tension`**: stiffness (> 0)
|
|
48
|
+
- **`damping`**: friction (>= 0)
|
|
49
|
+
- **`dampingRatio`**: 0 = undamped, < 1 = bouncy, 1 = critically damped, > 1 = overdamped
|
|
50
|
+
- **`bounce`**: a friendlier alias for damping ratio, from -1 (overdamped) to 1 (max bounce)
|
|
51
|
+
- **`duration`**: settle time in ms — the spring rests at this time, within a frame (a bouncy config can rest up to one oscillation earlier). Assumes an initial displacement of 1. Pass `displacement` matching your animation range for accurate timing
|
|
52
|
+
- **`mass`**: defaults to 1
|
|
53
|
+
- **`precision`**: decimal places of the resting threshold (default 2). A spring is _resting_ once its remaining motion cannot reach half a unit in the last place (0.005 at the default). Coily never rounds values. Set `precision` to match your domain's resolution (see [PRECISION.md](https://github.com/tkofh/coily/blob/main/PRECISION.md))
|
|
54
|
+
- **`arrival`**: what the motion does when the value reaches the target. `'passthrough'` (default) swings through and settles; `'stop'` ends the motion exactly on the target the first time it gets there, however bouncy the config; a number between -1 and 1 scales the velocity at every crossing — negative values rebound. Crossings are solved exactly, never sampled frame by frame (see [PRECISION.md](https://github.com/tkofh/coily/blob/main/PRECISION.md))
|
|
53
55
|
|
|
54
56
|
Without a config, springs are critically damped with a ~500ms settle time.
|
|
55
57
|
|
|
56
58
|
## Spring API
|
|
57
59
|
|
|
58
|
-
- `spring.target
|
|
59
|
-
- `spring.value
|
|
60
|
-
- `spring.velocity
|
|
61
|
-
- `spring.jumpTo(v)
|
|
62
|
-
- `spring.config
|
|
63
|
-
- `spring.isResting`, `spring.timeRemaining
|
|
64
|
-
- `spring.settled
|
|
65
|
-
- `spring.onUpdate(cb)` / `onStart(cb)` / `onStop(cb)` / `onDispose(cb)
|
|
66
|
-
- `spring.dispose()
|
|
60
|
+
- `spring.target`: set to animate toward a new value
|
|
61
|
+
- `spring.value`: current value, writable to displace the spring
|
|
62
|
+
- `spring.velocity`: current velocity, writable to fling
|
|
63
|
+
- `spring.jumpTo(v)`: snap to a value with no animation
|
|
64
|
+
- `spring.config`: assign a new `SpringDefinition`, or `null` to revert to the default
|
|
65
|
+
- `spring.isResting`, `spring.timeRemaining`: settle state and ms until rest — solved from the motion, not estimated (see [PRECISION.md](https://github.com/tkofh/coily/blob/main/PRECISION.md))
|
|
66
|
+
- `spring.settled`: a promise that resolves when the spring next comes to rest (immediately if already resting). Retargeting mid-flight extends the wait; disposing resolves it. `await spring.settled` to sequence animations
|
|
67
|
+
- `spring.onUpdate(cb)` / `onStart(cb)` / `onStop(cb)` / `onDispose(cb)`: subscribe. Each returns an unsubscribe function. `start` fires when the spring leaves rest, `stop` when it settles. The two always alternate, and retargeting mid-flight fires neither
|
|
68
|
+
- `spring.dispose()`: release the spring (calling it twice is a no-op)
|
|
67
69
|
|
|
68
70
|
### Chaining
|
|
69
71
|
|
|
70
|
-
A spring can follow another spring's live value instead of a fixed number:
|
|
72
|
+
A spring can follow another spring's live value instead of a fixed number: assign a spring to `target`, or pass one straight to `createSpring` to follow from birth. `mapSpring` derives new followable values from existing ones (offsets, mirrors, clamps, any pure function of the value):
|
|
71
73
|
|
|
72
74
|
```ts
|
|
73
75
|
const leader = system.createSpring(0)
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
+
|
|
77
|
+
const follower = system.createSpring(leader)
|
|
78
|
+
const trailing = system.createSpring(mapSpring(leader, (v) => v + 20))
|
|
79
|
+
const mirrored = system.createSpring(mapSpring(leader, (v) => -v))
|
|
76
80
|
```
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
A follower's config is its own: following changes what a spring chases, never how it moves. To match a leader's feel, pass its config at creation — `system.createSpring(leader, leader.config)` — or share a `defineSpring` constant. Assigning a number to `target` unfollows.
|
|
83
|
+
|
|
84
|
+
`mapSpring` also combines several springs: pass a shape (a plain object or array with springs at the leaves, nested arbitrarily) and a function of their values:
|
|
79
85
|
|
|
80
|
-
|
|
86
|
+
```ts
|
|
87
|
+
const x = system.createSpring(0)
|
|
88
|
+
const y = system.createSpring(0)
|
|
89
|
+
|
|
90
|
+
const distance = system.createSpring(mapSpring({ x, y }, ({ x, y }) => Math.hypot(x, y)))
|
|
91
|
+
```
|
|
81
92
|
|
|
82
|
-
|
|
93
|
+
Composite springs (below) are sources of their whole value shape, so one map can derive a scalar from every channel at once:
|
|
83
94
|
|
|
84
95
|
```ts
|
|
85
|
-
const
|
|
96
|
+
const point = system.createSpring({ x: 3, y: 4 })
|
|
97
|
+
|
|
98
|
+
const magnitude = system.createSpring(mapSpring(point, ({ x, y }) => Math.hypot(x, y)))
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A mapped value is a `SpringSource`, the contract `target` accepts and every `Spring` implements. A `CompositeSpring` is a source _of its shape_: `mapSpring` reads it, alone or at the leaves of a shape, but only scalar sources can be followed directly.
|
|
102
|
+
|
|
103
|
+
Chains advance in dependency order: each frame, a spring moves after the springs it follows — whatever order anything was created or wired in — so a follower always chases its leader's current value rather than last frame's. Update events fire once per spring per frame, after every spring has moved, in that same leader-first order. A callback that reads another spring sees its final value for the frame, and a spring retargeted from inside a callback starts moving on the next frame.
|
|
86
104
|
|
|
87
|
-
|
|
105
|
+
Following is accurate within the frame, too: a follower integrates against the path its leader actually traveled, not a value sampled once at the frame boundary, and when a single frame carries more motion than the system's `couplingTolerance` allows (0.1 value units by default), the system splits that frame into internal sub-steps. Dropped frames and teleporting targets refine themselves; update events still fire once per frame. The result is that a chain keeps the same shape at 30fps as at 120fps — see https://github.com/tkofh/coily/blob/main/PRECISION.md for the model.
|
|
106
|
+
|
|
107
|
+
Follow graphs may contain cycles: retargets never re-emit, so nothing loops within a frame. Mutual followers converge to a shared value — the same value at any frame rate. A cycle has no order that puts every member after its leaders, so members advance in creation order and the edge that closes the loop chases one frame behind. A cycle whose maps expand (`(v) => v + 10` in both directions) chases forever and never rests, keeping the system awake until you break the cycle.
|
|
108
|
+
|
|
109
|
+
### Composites
|
|
110
|
+
|
|
111
|
+
`createSpring` also takes any numeric shape: a plain object or array whose leaves are all numbers, nested arbitrarily. Each leaf becomes an independent channel behind one composite API, a `CompositeSpring`:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const spring = system.createSpring({ position: { x: 0, y: 0 }, opacity: 1 })
|
|
115
|
+
|
|
116
|
+
spring.target = { position: { x: 100 } } // a partial target leaves other channels alone
|
|
88
117
|
spring.value // { position: { x, y }, opacity } — a stable, read-only mirror
|
|
89
118
|
```
|
|
90
119
|
|
|
91
|
-
The shape is fixed at creation, and unknown channels throw with their path (`position.z`). `value`, `velocity`, and `jumpTo` take the same partial shapes.
|
|
120
|
+
The shape is fixed at creation, and unknown channels throw with their path (`position.z`). `value`, `velocity`, and `jumpTo` take the same partial shapes. Composites coalesce their events: `update` fires at most once per frame with every channel in its final per-frame state, and `stop` always lands after that frame's final `update`. `settled` and reduced motion compose channel-wise.
|
|
92
121
|
|
|
93
|
-
|
|
122
|
+
The `Shape` type validates shapes at compile time too (interfaces like your own `Vector2` work without index signatures) and rejects non-numeric, optional, or `undefined`-typed channels where they're declared.
|
|
94
123
|
|
|
95
|
-
Configs apply per channel. Pass a single `
|
|
124
|
+
Configs apply per channel. Pass a single `SpringDefinition` for every channel, or a shape mirroring the value with configs (or `null` to revert) at any level. A config at a subtree covers every channel below it:
|
|
96
125
|
|
|
97
126
|
```ts
|
|
98
127
|
spring.config = { position: stiff, opacity: defineSpring({ duration: 300, dampingRatio: 1 }) }
|
|
99
128
|
```
|
|
100
129
|
|
|
101
|
-
Configs are `
|
|
130
|
+
Configs are `SpringDefinition` instances, built with `defineSpring`. Config positions don't accept bare option objects: a plain object is always a per-channel shape, so any non-config leaf it reaches throws with its path.
|
|
102
131
|
|
|
103
|
-
|
|
132
|
+
Composite springs chain channel-wise. Assign another composite spring of the exact same shape to follow it whole, or name channels individually: each channel of a partial target takes a number or a scalar `SpringSource`. While following, a partial target detaches only the channels it names:
|
|
104
133
|
|
|
105
134
|
```ts
|
|
106
135
|
follower.target = leader
|
|
107
|
-
|
|
136
|
+
|
|
137
|
+
// or mix numbers and live sources per channel
|
|
138
|
+
follower.target = {
|
|
139
|
+
opacity: 0.5,
|
|
140
|
+
position: { x: mapSpring(leader, ({ position }) => -position.x) },
|
|
141
|
+
}
|
|
108
142
|
```
|
|
109
143
|
|
|
110
144
|
## Reduced motion
|
|
111
145
|
|
|
112
|
-
Coily respects `prefers-reduced-motion` by default. When it's active, springs snap to their targets instead of animating: retargets and value writes apply instantly,
|
|
146
|
+
Coily respects `prefers-reduced-motion` by default. When it's active, springs snap to their targets instead of animating: retargets and value writes apply instantly, and springs ignore velocity impulses. Events stay coherent (one `update` per change, no `start`/`stop`, and `settled` resolves immediately), so code written against the animated path keeps working.
|
|
113
147
|
|
|
114
|
-
Control it with the `reducedMotion` system option: `'
|
|
148
|
+
Control it with the `reducedMotion` system option: `'always'`, `'never'`, or the default `'user'`, which follows the OS setting — a live change finishes in-flight animations instantly. Read `system.reducedMotion` to gate decorative effects (particles, flourishes) in your own code.
|
|
115
149
|
|
|
116
150
|
## Vue
|
|
117
151
|
|
|
118
|
-
Call `useSpringSystem()` once near the root of your app. It returns the spring system provided by this component or an ancestor
|
|
152
|
+
Call `useSpringSystem()` once near the root of your app. It returns the spring system provided by this component or an ancestor. If none exists, it creates one, provides it to descendants, and starts/stops it with the component lifecycle. It's idempotent, and options apply only when a system is created:
|
|
119
153
|
|
|
120
154
|
```ts
|
|
121
155
|
import { useSpringSystem } from 'coily/vue'
|
|
@@ -140,13 +174,15 @@ const x = useSpring(target, { duration: 500, bounce: 0.3 })
|
|
|
140
174
|
</template>
|
|
141
175
|
```
|
|
142
176
|
|
|
143
|
-
`useSpring` returns a `SpringRef`: a writable ref of the animated value with `velocity`, `isResting`, and `timeRemaining` refs plus `jumpTo()` attached. The target can be a ref, a getter, or another `SpringRef` (which chains the springs). Options are also reactive
|
|
177
|
+
`useSpring` returns a `SpringRef`: a writable ref of the animated value with `velocity`, `isResting`, and `timeRemaining` refs plus `jumpTo()` attached. The target can be a ref, a getter, or another `SpringRef` (which chains the springs). Options are also reactive: swap configs and the spring reconfigures in place.
|
|
178
|
+
|
|
179
|
+
A `SpringRef` is also a `SpringSource`, and `useSpring` follows any source: chain through `mapSpring` (`useSpring(mapSpring({ x, y }, ({ x, y }) => Math.hypot(x, y)))`), or pass a getter of a source to switch leaders reactively (`useSpring(() => (split.value ? left : right))`). Following bypasses Vue reactivity — followers subscribe to the backing spring directly — so an effect or getter never gains a dependency on a leader's animation.
|
|
144
180
|
|
|
145
|
-
Numeric shapes work the same way: pass a record or array (plain, ref, or getter) and get a `
|
|
181
|
+
Numeric shapes work the same way: pass a record or array (plain, ref, or getter) and get a `CompositeSpringRef`. Reads are the deep-readonly composite value, writes take partial shapes, options accept reactive per-channel config shapes, and passing another `CompositeSpringRef` follows it channel-wise. Deeply reactive targets retarget on nested mutation. For several _independent_ scalar springs, map over the targets (composables are loop-safe): `targets.map((t) => useSpring(t))`.
|
|
146
182
|
|
|
147
183
|
There's also a renderless `<SpringValue :target="n">` component exposing `{ value, velocity, isResting, timeRemaining, jumpTo }` through its default slot.
|
|
148
184
|
|
|
149
|
-
|
|
185
|
+
`useSpringPool()` covers imperative work: a dynamic set of springs created and disposed at arbitrary times (particles, per-item effects). It returns `createSpring` bound to the provided system. The pool disposes every spring created through it when the component's scope is torn down, so leaked motions are structurally impossible. Disposing a spring manually before that is fine.
|
|
150
186
|
|
|
151
187
|
## Nuxt
|
|
152
188
|
|
|
@@ -164,7 +200,7 @@ The module provides a spring system for the whole app (started on the client), a
|
|
|
164
200
|
|
|
165
201
|
## Timing
|
|
166
202
|
|
|
167
|
-
The built-in ticker advances springs once per displayed frame, so motion is as smooth as the screen it runs on
|
|
203
|
+
The built-in ticker advances springs once per displayed frame, so motion is as smooth as the screen it runs on: 60Hz, 120Hz, or adaptive sync. The loop sleeps while every spring is at rest (an idle system schedules no frames) and wakes on the next write. Set `fps` (option/property) to cap the rate: capped ticks land on whole display frames and still receive the true elapsed time. The ticker clamps large frame gaps (e.g. returning from a backgrounded tab) via `lagThreshold` (default 500ms) and `adjustedLag` (default 33ms), so springs don't teleport. For manual stepping, skip `system.start()` and call `system.advance(dtMs)` yourself.
|
|
168
204
|
|
|
169
205
|
## License
|
|
170
206
|
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
//#region src/config.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A spring's instantaneous motion, measured relative to its target.
|
|
4
|
+
* `SpringDefinition.computeTimeRemaining` takes one; build it from a live
|
|
5
|
+
* spring as `{ position: spring.value - spring.target, velocity: spring.velocity }`.
|
|
6
|
+
*/
|
|
7
|
+
interface SpringState {
|
|
8
|
+
/** Displacement from the target in value units. 0 means at the target. */
|
|
9
|
+
readonly position: number;
|
|
10
|
+
/** Rate of change of the displacement, in value units per second. */
|
|
11
|
+
readonly velocity: number;
|
|
12
|
+
}
|
|
13
|
+
interface BaseOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Decimal places of the resting threshold. A spring rests once its
|
|
16
|
+
* remaining motion cannot reach half a unit in the last place —
|
|
17
|
+
* `0.5 * 10^-precision`, or 0.005 at the default.
|
|
18
|
+
*
|
|
19
|
+
* Values are never rounded; precision only decides rest. See
|
|
20
|
+
* https://github.com/tkofh/coily/blob/main/PRECISION.md.
|
|
21
|
+
*
|
|
22
|
+
* @default 2
|
|
23
|
+
*/
|
|
24
|
+
readonly precision?: number | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* What the motion does when the value reaches the target, as a
|
|
27
|
+
* multiplier applied to the velocity at every crossing.
|
|
28
|
+
*
|
|
29
|
+
* - `'passthrough'` — multiplier 1. The motion is untouched: the value
|
|
30
|
+
* swings through the target and settles as its damping dictates.
|
|
31
|
+
* - `'stop'` — multiplier 0. Motion ends at the target the first time
|
|
32
|
+
* the value gets there, with no overshoot, however bouncy the config.
|
|
33
|
+
* - A number between -1 and 1 sets the multiplier directly. Negative
|
|
34
|
+
* values rebound: `-0.75` reverses the motion with three quarters of
|
|
35
|
+
* its speed, at every return. Values between 0 and 1 pass through
|
|
36
|
+
* slowed.
|
|
37
|
+
*
|
|
38
|
+
* Crossings are solved from the motion's closed form, never sampled
|
|
39
|
+
* frame by frame, so a `'stop'` lands the value exactly on the target
|
|
40
|
+
* whatever the frame timing. See
|
|
41
|
+
* https://github.com/tkofh/coily/blob/main/PRECISION.md.
|
|
42
|
+
*
|
|
43
|
+
* @default 'passthrough'
|
|
44
|
+
*/
|
|
45
|
+
readonly arrival?: 'passthrough' | 'stop' | number | undefined;
|
|
46
|
+
}
|
|
47
|
+
interface WithMass {
|
|
48
|
+
/**
|
|
49
|
+
* Mass of the moving value. Heavier springs accelerate more slowly and
|
|
50
|
+
* carry more momentum through the target. Must be greater than 0.
|
|
51
|
+
* @default 1
|
|
52
|
+
*/
|
|
53
|
+
readonly mass?: number | undefined;
|
|
54
|
+
}
|
|
55
|
+
interface WithoutMass {
|
|
56
|
+
/**
|
|
57
|
+
* Mass is derived from the other parameters in this input shape
|
|
58
|
+
* and cannot be provided.
|
|
59
|
+
*/
|
|
60
|
+
readonly mass?: undefined;
|
|
61
|
+
}
|
|
62
|
+
interface WithTension {
|
|
63
|
+
/**
|
|
64
|
+
* Spring stiffness. Higher tension pulls toward the target harder,
|
|
65
|
+
* making the whole motion faster. Must be greater than 0.
|
|
66
|
+
*/
|
|
67
|
+
readonly tension: number;
|
|
68
|
+
}
|
|
69
|
+
interface WithDamping {
|
|
70
|
+
/**
|
|
71
|
+
* Friction strength. Higher damping bleeds off velocity faster and
|
|
72
|
+
* reduces bounce; at 0 the spring oscillates forever. Must be greater
|
|
73
|
+
* than or equal to 0.
|
|
74
|
+
*/
|
|
75
|
+
readonly damping: number;
|
|
76
|
+
}
|
|
77
|
+
interface WithDampingRatio {
|
|
78
|
+
/**
|
|
79
|
+
* Damping as a fraction of critical damping — the character of the
|
|
80
|
+
* motion, independent of its speed. Below 1 the spring overshoots and
|
|
81
|
+
* bounces. At 1 it settles as fast as possible without overshooting.
|
|
82
|
+
* Above 1 it settles slower, still without overshooting. At 0 it never
|
|
83
|
+
* settles. Must be greater than or equal to 0.
|
|
84
|
+
*/
|
|
85
|
+
readonly dampingRatio: number;
|
|
86
|
+
}
|
|
87
|
+
interface WithBounce {
|
|
88
|
+
/**
|
|
89
|
+
* Bounciness, converted to a damping ratio as `1 - bounce`. 0 settles
|
|
90
|
+
* as fast as possible without overshooting. Positive values overshoot
|
|
91
|
+
* and bounce; 1, maximum bounce, is clamped just short of undamped so
|
|
92
|
+
* the spring still settles. Negative values settle without bounce, more
|
|
93
|
+
* slowly. Must be between -1 and 1.
|
|
94
|
+
*/
|
|
95
|
+
readonly bounce: number;
|
|
96
|
+
}
|
|
97
|
+
interface WithDuration {
|
|
98
|
+
/**
|
|
99
|
+
* Settle time in milliseconds. The spring is tuned so it rests at this
|
|
100
|
+
* time, assuming it starts `displacement` away from the target: rest
|
|
101
|
+
* lands within a frame of the requested duration, and a bouncy config
|
|
102
|
+
* can rest up to one oscillation earlier. Must be greater than 0.
|
|
103
|
+
*/
|
|
104
|
+
readonly duration: number;
|
|
105
|
+
}
|
|
106
|
+
interface WithDisplacement {
|
|
107
|
+
/**
|
|
108
|
+
* The initial displacement the duration tuning assumes. Set it to your
|
|
109
|
+
* animation's range — 300 when animating 0 to 300 — so the settle
|
|
110
|
+
* timing holds; timing skews as the real displacement diverges from
|
|
111
|
+
* this value. Must not be 0.
|
|
112
|
+
* @default 1
|
|
113
|
+
*/
|
|
114
|
+
readonly displacement?: number | undefined;
|
|
115
|
+
}
|
|
116
|
+
interface DirectOptions extends BaseOptions, WithMass, WithTension, WithDamping {}
|
|
117
|
+
interface TensionRatioOptions extends BaseOptions, WithMass, WithTension, WithDampingRatio {}
|
|
118
|
+
interface TensionBounceOptions extends BaseOptions, WithMass, WithTension, WithBounce {}
|
|
119
|
+
interface DampingRatioOptions extends BaseOptions, WithMass, WithDamping, WithDampingRatio {}
|
|
120
|
+
interface DampingBounceOptions extends BaseOptions, WithMass, WithDamping, WithBounce {}
|
|
121
|
+
interface TensionDampingRatioOptions extends BaseOptions, WithoutMass, WithTension, WithDamping, WithDampingRatio {}
|
|
122
|
+
interface TensionDampingBounceOptions extends BaseOptions, WithoutMass, WithTension, WithDamping, WithBounce {}
|
|
123
|
+
interface DurationOptions extends BaseOptions, WithMass, WithDampingRatio, WithDuration, WithDisplacement {}
|
|
124
|
+
interface BounceDurationOptions extends BaseOptions, WithMass, WithBounce, WithDuration, WithDisplacement {}
|
|
125
|
+
interface TensionDurationOptions extends BaseOptions, WithoutMass, WithTension, WithDampingRatio, WithDuration, WithDisplacement {}
|
|
126
|
+
interface TensionBounceDurationOptions extends BaseOptions, WithoutMass, WithTension, WithBounce, WithDuration, WithDisplacement {}
|
|
127
|
+
interface DampingDurationOptions extends BaseOptions, WithoutMass, WithDamping, WithDampingRatio, WithDuration, WithDisplacement {}
|
|
128
|
+
interface DampingBounceDurationOptions extends BaseOptions, WithoutMass, WithDamping, WithBounce, WithDuration, WithDisplacement {}
|
|
129
|
+
/** Every option key accepted by at least one `defineSpring` input shape. */
|
|
130
|
+
type SpringOptionKeys = 'mass' | 'tension' | 'damping' | 'dampingRatio' | 'bounce' | 'duration' | 'displacement' | 'precision' | 'arrival';
|
|
131
|
+
/**
|
|
132
|
+
* Marks every option key foreign to `T` as "absent or undefined", so mixing
|
|
133
|
+
* properties from different input shapes fails to type-check instead of
|
|
134
|
+
* silently resolving to an unintended shape at runtime.
|
|
135
|
+
*/
|
|
136
|
+
type Exact<T> = T & { [K in Exclude<SpringOptionKeys, keyof T>]?: undefined; };
|
|
137
|
+
/**
|
|
138
|
+
* Every input `defineSpring` accepts: one of the documented parameter
|
|
139
|
+
* combinations, exactly as written — keys from different combinations
|
|
140
|
+
* don't mix.
|
|
141
|
+
*/
|
|
142
|
+
type SpringDefinitionOptions = Exact<DirectOptions> | Exact<TensionRatioOptions> | Exact<TensionBounceOptions> | Exact<DampingRatioOptions> | Exact<DampingBounceOptions> | Exact<TensionDampingRatioOptions> | Exact<TensionDampingBounceOptions> | Exact<DurationOptions> | Exact<BounceDurationOptions> | Exact<TensionDurationOptions> | Exact<TensionBounceDurationOptions> | Exact<DampingDurationOptions> | Exact<DampingBounceDurationOptions>;
|
|
143
|
+
/**
|
|
144
|
+
* Immutable, resolved spring parameters. Whatever `defineSpring` input
|
|
145
|
+
* shape produced it, a config carries concrete `mass`, `tension`,
|
|
146
|
+
* `damping`, and `precision`, plus the quantities derived from them.
|
|
147
|
+
* Instances are frozen and safe to share between springs; to change a
|
|
148
|
+
* spring's behavior, assign a new config via `spring.config`.
|
|
149
|
+
*/
|
|
150
|
+
declare class SpringDefinition {
|
|
151
|
+
/** The config for springs created without one: critically damped, settling in about 500ms. */
|
|
152
|
+
static readonly default: SpringDefinition;
|
|
153
|
+
/** Mass of the moving value, as provided or derived. */
|
|
154
|
+
readonly mass: number;
|
|
155
|
+
/** Spring stiffness, as provided or derived. */
|
|
156
|
+
readonly tension: number;
|
|
157
|
+
/** Friction strength, as provided or derived. */
|
|
158
|
+
readonly damping: number;
|
|
159
|
+
/** Decimal places of the resting threshold — see `restingMagnitude`. */
|
|
160
|
+
readonly precision: number;
|
|
161
|
+
/**
|
|
162
|
+
* Velocity multiplier applied when the value crosses the target, as
|
|
163
|
+
* provided or resolved from its named form. 1 (`'passthrough'`) leaves
|
|
164
|
+
* the motion untouched, 0 (`'stop'`) ends it at the target, negative
|
|
165
|
+
* values rebound.
|
|
166
|
+
*/
|
|
167
|
+
readonly arrival: number;
|
|
168
|
+
/** Oscillation rate with no damping: `sqrt(tension / mass)`, in radians per second. */
|
|
169
|
+
readonly naturalFrequency: number;
|
|
170
|
+
/** The damping at which bounce disappears: `2 * mass * naturalFrequency`. */
|
|
171
|
+
readonly criticalDamping: number;
|
|
172
|
+
/**
|
|
173
|
+
* `damping / criticalDamping`. Below 1 the spring overshoots and
|
|
174
|
+
* bounces, at 1 it settles fastest without overshooting, above 1 it
|
|
175
|
+
* settles slower.
|
|
176
|
+
*/
|
|
177
|
+
readonly dampingRatio: number;
|
|
178
|
+
/** Resting threshold in value units: half a unit in the last `precision` place. */
|
|
179
|
+
readonly restingMagnitude: number;
|
|
180
|
+
constructor(input: SpringDefinitionOptions);
|
|
181
|
+
/**
|
|
182
|
+
* Computes when a spring with this config comes to rest from `state`,
|
|
183
|
+
* in ms.
|
|
184
|
+
*
|
|
185
|
+
* The time is solved from the motion's decay bound rather than
|
|
186
|
+
* estimated: the spring is resting at the first tick at or after it.
|
|
187
|
+
* An underdamped spring can also rest earlier — its rest amplitude
|
|
188
|
+
* pulses, and a frame that samples a dip inside the resting threshold
|
|
189
|
+
* rests there — so read the value as "no later than", tight in
|
|
190
|
+
* practice. See
|
|
191
|
+
* https://github.com/tkofh/coily/blob/main/PRECISION.md.
|
|
192
|
+
*
|
|
193
|
+
* Returns 0 when `state` is already inside the resting threshold.
|
|
194
|
+
* Returns Infinity when the motion never settles: a `dampingRatio` of
|
|
195
|
+
* 0 whose `arrival` neither stops nor slows it. When `arrival` is 0
|
|
196
|
+
* the result is capped at the first target crossing, where such a
|
|
197
|
+
* spring rests exactly; other multipliers fold their per-crossing
|
|
198
|
+
* velocity loss into the time.
|
|
199
|
+
*/
|
|
200
|
+
computeTimeRemaining(state: SpringState): number;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Creates a config from direct physical parameters: you set stiffness
|
|
204
|
+
* and friction exactly.
|
|
205
|
+
*/
|
|
206
|
+
declare function defineSpring(input: Exact<DirectOptions>): SpringDefinition;
|
|
207
|
+
/**
|
|
208
|
+
* Creates a config from stiffness and motion character. Damping is
|
|
209
|
+
* derived as `2 * dampingRatio * sqrt(mass * tension)`.
|
|
210
|
+
*/
|
|
211
|
+
declare function defineSpring(input: Exact<TensionRatioOptions>): SpringDefinition;
|
|
212
|
+
/**
|
|
213
|
+
* Creates a config from stiffness and bounciness. Damping is derived to
|
|
214
|
+
* match the requested bounce, from -1 (no bounce, slow settle) to 1
|
|
215
|
+
* (maximum bounce).
|
|
216
|
+
*/
|
|
217
|
+
declare function defineSpring(input: Exact<TensionBounceOptions>): SpringDefinition;
|
|
218
|
+
/**
|
|
219
|
+
* Creates a config from friction and motion character. Tension is
|
|
220
|
+
* derived as `damping^2 / (4 * dampingRatio^2 * mass)`; `dampingRatio`
|
|
221
|
+
* must be greater than 0.
|
|
222
|
+
*/
|
|
223
|
+
declare function defineSpring(input: Exact<DampingRatioOptions>): SpringDefinition;
|
|
224
|
+
/**
|
|
225
|
+
* Creates a config from friction and bounciness. Tension is derived to
|
|
226
|
+
* match the requested bounce; `bounce` must be less than 1.
|
|
227
|
+
*/
|
|
228
|
+
declare function defineSpring(input: Exact<DampingBounceOptions>): SpringDefinition;
|
|
229
|
+
/**
|
|
230
|
+
* Creates a config from all three physical knobs. Mass is derived as
|
|
231
|
+
* `damping^2 / (4 * dampingRatio^2 * tension)` and cannot be provided;
|
|
232
|
+
* `dampingRatio` must be greater than 0.
|
|
233
|
+
*/
|
|
234
|
+
declare function defineSpring(input: Exact<TensionDampingRatioOptions>): SpringDefinition;
|
|
235
|
+
/**
|
|
236
|
+
* Creates a config from all three physical knobs. Mass is derived from
|
|
237
|
+
* tension, damping, and the requested bounce, and cannot be provided;
|
|
238
|
+
* `bounce` must be less than 1.
|
|
239
|
+
*/
|
|
240
|
+
declare function defineSpring(input: Exact<TensionDampingBounceOptions>): SpringDefinition;
|
|
241
|
+
/**
|
|
242
|
+
* Tunes the spring to rest at `duration` milliseconds, with
|
|
243
|
+
* `dampingRatio` setting the motion's character. Tension and damping are
|
|
244
|
+
* both derived.
|
|
245
|
+
*
|
|
246
|
+
* The timing assumes the motion starts `displacement` (default 1) away
|
|
247
|
+
* from the target — pass a `displacement` matching your animation range
|
|
248
|
+
* for accurate timing.
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```ts
|
|
252
|
+
* // Tuned for a 300px move; rests within 750ms
|
|
253
|
+
* defineSpring({ duration: 750, dampingRatio: 1, displacement: 300 })
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
declare function defineSpring(input: Exact<DurationOptions>): SpringDefinition;
|
|
257
|
+
/**
|
|
258
|
+
* Tunes the spring to rest at `duration` milliseconds, with
|
|
259
|
+
* `bounce` setting how oscillatory the settling motion is. Tension and
|
|
260
|
+
* damping are both derived.
|
|
261
|
+
*
|
|
262
|
+
* The timing assumes the motion starts `displacement` (default 1) away
|
|
263
|
+
* from the target — pass a `displacement` matching your animation range
|
|
264
|
+
* for accurate timing.
|
|
265
|
+
*/
|
|
266
|
+
declare function defineSpring(input: Exact<BounceDurationOptions>): SpringDefinition;
|
|
267
|
+
/**
|
|
268
|
+
* Tunes a spring of the given stiffness to rest at `duration`
|
|
269
|
+
* milliseconds. Mass is derived from tension and the computed natural
|
|
270
|
+
* frequency, then damping from mass and `dampingRatio`; mass cannot be
|
|
271
|
+
* provided.
|
|
272
|
+
*
|
|
273
|
+
* The timing assumes the motion starts `displacement` (default 1) away
|
|
274
|
+
* from the target — pass a `displacement` matching your animation range
|
|
275
|
+
* for accurate timing.
|
|
276
|
+
*/
|
|
277
|
+
declare function defineSpring(input: Exact<TensionDurationOptions>): SpringDefinition;
|
|
278
|
+
/**
|
|
279
|
+
* Tunes a spring of the given stiffness to rest at `duration`
|
|
280
|
+
* milliseconds, with `bounce` setting how oscillatory the settling motion
|
|
281
|
+
* is. Mass is derived from tension and the computed natural frequency,
|
|
282
|
+
* and cannot be provided.
|
|
283
|
+
*
|
|
284
|
+
* The timing assumes the motion starts `displacement` (default 1) away
|
|
285
|
+
* from the target — pass a `displacement` matching your animation range
|
|
286
|
+
* for accurate timing.
|
|
287
|
+
*/
|
|
288
|
+
declare function defineSpring(input: Exact<TensionBounceDurationOptions>): SpringDefinition;
|
|
289
|
+
/**
|
|
290
|
+
* Tunes a spring of the given friction to rest at `duration`
|
|
291
|
+
* milliseconds. Mass is derived from damping, `dampingRatio`, and the
|
|
292
|
+
* computed natural frequency, then tension from mass; mass cannot be
|
|
293
|
+
* provided.
|
|
294
|
+
*
|
|
295
|
+
* The timing assumes the motion starts `displacement` (default 1) away
|
|
296
|
+
* from the target — pass a `displacement` matching your animation range
|
|
297
|
+
* for accurate timing.
|
|
298
|
+
*/
|
|
299
|
+
declare function defineSpring(input: Exact<DampingDurationOptions>): SpringDefinition;
|
|
300
|
+
/**
|
|
301
|
+
* Tunes a spring of the given friction to rest at `duration`
|
|
302
|
+
* milliseconds, with `bounce` setting how oscillatory the settling motion
|
|
303
|
+
* is. Mass is derived from damping and the computed natural frequency,
|
|
304
|
+
* and cannot be provided.
|
|
305
|
+
*
|
|
306
|
+
* The timing assumes the motion starts `displacement` (default 1) away
|
|
307
|
+
* from the target — pass a `displacement` matching your animation range
|
|
308
|
+
* for accurate timing.
|
|
309
|
+
*/
|
|
310
|
+
declare function defineSpring(input: Exact<DampingBounceDurationOptions>): SpringDefinition;
|
|
311
|
+
//#endregion
|
|
312
|
+
export { defineSpring as i, SpringDefinitionOptions as n, SpringState as r, SpringDefinition as t };
|