coily 0.14.0 → 0.16.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 +133 -51
- package/dist/config-B8M2xp26.d.ts +295 -0
- package/dist/css.d.ts +203 -0
- package/dist/css.js +318 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +99 -53
- package/dist/nuxt.d.ts +24 -15
- package/dist/nuxt.js +16 -5
- package/dist/solver-CAXuxdKj.js +243 -0
- package/dist/system-C7PbI4L0.d.ts +1213 -0
- package/dist/system-WejqGkfv.js +2920 -0
- package/dist/vue.d.ts +224 -128
- package/dist/vue.js +161 -41
- package/package.json +2 -1
- package/dist/system-BXxAHM2a.js +0 -1758
- package/dist/system-Ci-R4RaI.d.ts +0 -1032
package/dist/css.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { i as State, n as OverdampedSolver, o as invariant, r as UnderdampedSolver, t as CriticallyDampedSolver } from "./solver-CAXuxdKj.js";
|
|
2
|
+
//#region src/css.ts
|
|
3
|
+
function pickSolver(config, state) {
|
|
4
|
+
const solver = config.dampingRatio < 1 ? new UnderdampedSolver(state) : config.dampingRatio === 1 ? new CriticallyDampedSolver(state) : new OverdampedSolver(state);
|
|
5
|
+
solver.configure(config);
|
|
6
|
+
return solver;
|
|
7
|
+
}
|
|
8
|
+
function walk(config, displacement, spanSec, scale, stopAtRest, velocity = 0) {
|
|
9
|
+
const state = new State(config, displacement, velocity);
|
|
10
|
+
const solver = pickSolver(config, state);
|
|
11
|
+
const period = 2 * Math.PI / config.naturalFrequency;
|
|
12
|
+
const dt = Math.max(1e-4, Math.min(period / 48, spanSec / 160));
|
|
13
|
+
const points = [{
|
|
14
|
+
x: 0,
|
|
15
|
+
y: 0
|
|
16
|
+
}];
|
|
17
|
+
let t = 0;
|
|
18
|
+
while (spanSec - t > 1e-9) {
|
|
19
|
+
const step = Math.min(dt, spanSec - t);
|
|
20
|
+
solver.tick(step);
|
|
21
|
+
t += step;
|
|
22
|
+
points.push({
|
|
23
|
+
x: t / spanSec,
|
|
24
|
+
y: (1 - state.position / displacement) * scale
|
|
25
|
+
});
|
|
26
|
+
if (stopAtRest && state.isResting) break;
|
|
27
|
+
}
|
|
28
|
+
return points;
|
|
29
|
+
}
|
|
30
|
+
function buildEasing(points, maxError) {
|
|
31
|
+
const simplified = simplify(points, maxError);
|
|
32
|
+
return {
|
|
33
|
+
easing: format(simplified),
|
|
34
|
+
stops: simplified.length,
|
|
35
|
+
maxError: reconstructionError(points, simplified)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function buildSettle(config, displacement, durationMs, maxError, velocity = 0) {
|
|
39
|
+
const points = walk(config, displacement, durationMs / 1e3, 1, true, velocity);
|
|
40
|
+
const last = points[points.length - 1];
|
|
41
|
+
if (1 - last.x > 1e-6) points.push({
|
|
42
|
+
x: 1,
|
|
43
|
+
y: 1
|
|
44
|
+
});
|
|
45
|
+
else {
|
|
46
|
+
last.x = 1;
|
|
47
|
+
last.y = 1;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
mode: "settle",
|
|
51
|
+
duration: durationMs,
|
|
52
|
+
...buildEasing(points, maxError)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function buildLoop(config, displacement, maxError) {
|
|
56
|
+
const halfSec = Math.PI / config.naturalFrequency;
|
|
57
|
+
const points = walk(config, displacement, halfSec, .5, false);
|
|
58
|
+
const last = points[points.length - 1];
|
|
59
|
+
last.x = 1;
|
|
60
|
+
last.y = 1;
|
|
61
|
+
return {
|
|
62
|
+
mode: "loop",
|
|
63
|
+
duration: halfSec * 1e3,
|
|
64
|
+
direction: "alternate",
|
|
65
|
+
iterations: "infinite",
|
|
66
|
+
...buildEasing(points, maxError)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function simplify(points, epsilon) {
|
|
70
|
+
const keep = Array.from({ length: points.length }, () => false);
|
|
71
|
+
keep[0] = true;
|
|
72
|
+
keep[points.length - 1] = true;
|
|
73
|
+
const stack = [[0, points.length - 1]];
|
|
74
|
+
while (stack.length > 0) {
|
|
75
|
+
const [lo, hi] = stack.pop();
|
|
76
|
+
const a = points[lo];
|
|
77
|
+
const b = points[hi];
|
|
78
|
+
const dx = b.x - a.x;
|
|
79
|
+
let worst = -1;
|
|
80
|
+
let worstIndex = -1;
|
|
81
|
+
for (let i = lo + 1; i < hi; i += 1) {
|
|
82
|
+
const p = points[i];
|
|
83
|
+
const f = dx === 0 ? 0 : (p.x - a.x) / dx;
|
|
84
|
+
const interpolated = a.y + f * (b.y - a.y);
|
|
85
|
+
const distance = Math.abs(p.y - interpolated);
|
|
86
|
+
if (distance > worst) {
|
|
87
|
+
worst = distance;
|
|
88
|
+
worstIndex = i;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (worst > epsilon && worstIndex !== -1) {
|
|
92
|
+
keep[worstIndex] = true;
|
|
93
|
+
stack.push([lo, worstIndex], [worstIndex, hi]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return points.filter((_, i) => keep[i]);
|
|
97
|
+
}
|
|
98
|
+
function trim(value, places) {
|
|
99
|
+
return Number(value.toFixed(places)).toString();
|
|
100
|
+
}
|
|
101
|
+
function format(points) {
|
|
102
|
+
return `linear(${points.map((point, i) => {
|
|
103
|
+
const output = trim(point.y, 5);
|
|
104
|
+
if (i === 0 || i === points.length - 1) return output;
|
|
105
|
+
return `${output} ${trim(point.x * 100, 2)}%`;
|
|
106
|
+
}).join(", ")})`;
|
|
107
|
+
}
|
|
108
|
+
function reconstructionError(dense, simplified) {
|
|
109
|
+
let worst = 0;
|
|
110
|
+
let j = 0;
|
|
111
|
+
for (const point of dense) {
|
|
112
|
+
while (j < simplified.length - 2 && simplified[j + 1].x < point.x) j += 1;
|
|
113
|
+
const a = simplified[j];
|
|
114
|
+
const b = simplified[j + 1];
|
|
115
|
+
const dx = b.x - a.x;
|
|
116
|
+
const f = dx === 0 ? 0 : (point.x - a.x) / dx;
|
|
117
|
+
worst = Math.max(worst, Math.abs(point.y - (a.y + f * (b.y - a.y))));
|
|
118
|
+
}
|
|
119
|
+
return worst;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Turns a spring config into a CSS `linear()` easing and the duration in
|
|
123
|
+
* ms to pair it with. A settling config returns a `'settle'` easing. A
|
|
124
|
+
* pure undamped config (`dampingRatio: 0` with passthrough `arrival`)
|
|
125
|
+
* never settles and returns a `'loop'` easing for an infinite alternating
|
|
126
|
+
* animation. Any other config that never settles, such as
|
|
127
|
+
* `dampingRatio: 0` with `arrival: -1`, returns a `'settle'` easing cut
|
|
128
|
+
* off at `maxDuration`.
|
|
129
|
+
*/
|
|
130
|
+
function springToLinear(config, options = {}) {
|
|
131
|
+
const displacement = options.displacement ?? 1;
|
|
132
|
+
const maxError = options.maxError ?? .0025;
|
|
133
|
+
const maxDuration = options.maxDuration ?? 1e4;
|
|
134
|
+
if (config.dampingRatio === 0 && config.arrival === 1) return buildLoop(config, displacement, maxError);
|
|
135
|
+
const settleTime = config.computeTimeRemaining({
|
|
136
|
+
position: displacement,
|
|
137
|
+
velocity: 0
|
|
138
|
+
});
|
|
139
|
+
return buildSettle(config, displacement, Math.min(settleTime, maxDuration), maxError);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Like `springToLinear`, but for a spring caught mid-flight. `state`
|
|
143
|
+
* carries the displacement from the target (`position`) and the current
|
|
144
|
+
* `velocity`, so the easing continues the motion instead of restarting it.
|
|
145
|
+
* Use it to regenerate an animation when its target changes. The curve
|
|
146
|
+
* depends on the velocity, so compute it per interruption instead of
|
|
147
|
+
* caching it per config.
|
|
148
|
+
*
|
|
149
|
+
* The result is always a `'settle'` easing, timed from `position`, so
|
|
150
|
+
* `options.displacement` is ignored. A config that never settles is cut
|
|
151
|
+
* off at `maxDuration`. Throws when `position` is 0 (within 1e-9), even if
|
|
152
|
+
* `velocity` is not.
|
|
153
|
+
*/
|
|
154
|
+
function springFromState(config, state, options = {}) {
|
|
155
|
+
const maxError = options.maxError ?? .0025;
|
|
156
|
+
const maxDuration = options.maxDuration ?? 1e4;
|
|
157
|
+
invariant(Math.abs(state.position) > 1e-9, "springFromState needs a nonzero position (the value and target must differ)");
|
|
158
|
+
const settleTime = config.computeTimeRemaining(state);
|
|
159
|
+
return buildSettle(config, state.position, Math.min(settleTime, maxDuration), maxError, state.velocity);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Advances a spring `state` by `elapsedMs` ms and returns the new
|
|
163
|
+
* displacement and velocity. `elapsedMs <= 0` returns a copy of `state`.
|
|
164
|
+
*
|
|
165
|
+
* The Web Animations API doesn't expose velocity, so to interrupt a running
|
|
166
|
+
* `element.animate`, advance the state it started from by its `currentTime`
|
|
167
|
+
* and hand the result to `springFromState`.
|
|
168
|
+
*/
|
|
169
|
+
function springStateAt(config, state, elapsedMs) {
|
|
170
|
+
if (elapsedMs <= 0) return {
|
|
171
|
+
position: state.position,
|
|
172
|
+
velocity: state.velocity
|
|
173
|
+
};
|
|
174
|
+
const live = new State(config, state.position, state.velocity);
|
|
175
|
+
pickSolver(config, live).tick(elapsedMs / 1e3);
|
|
176
|
+
return {
|
|
177
|
+
position: live.position,
|
|
178
|
+
velocity: live.velocity
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function resolveSpecs(config, specs, options) {
|
|
182
|
+
const withVelocity = specs.some((spec) => (spec.velocity ?? 0) !== 0);
|
|
183
|
+
invariant(!withVelocity || specs.length === 1, "velocity handoff is single-property: a from-state easing depends on that property, so run one animation per property");
|
|
184
|
+
const first = specs[0];
|
|
185
|
+
const easing = withVelocity ? springFromState(config, {
|
|
186
|
+
position: first.from - first.to,
|
|
187
|
+
velocity: first.velocity
|
|
188
|
+
}, options) : springToLinear(config, {
|
|
189
|
+
...options,
|
|
190
|
+
displacement: options.displacement ?? (Math.max(...specs.map((s) => Math.abs(s.to - s.from))) || 1)
|
|
191
|
+
});
|
|
192
|
+
const from = {};
|
|
193
|
+
const to = {};
|
|
194
|
+
for (const spec of specs) {
|
|
195
|
+
const format = spec.format ?? ((value) => `${Number(value.toFixed(3))}${spec.unit ?? ""}`);
|
|
196
|
+
const far = easing.mode === "loop" ? 2 * spec.to - spec.from : spec.to;
|
|
197
|
+
const start = format(spec.from);
|
|
198
|
+
const end = format(far);
|
|
199
|
+
from[spec.property] = from[spec.property] ? `${from[spec.property]} ${start}` : start;
|
|
200
|
+
to[spec.property] = to[spec.property] ? `${to[spec.property]} ${end}` : end;
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
easing,
|
|
204
|
+
from,
|
|
205
|
+
to
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function asArray(spec) {
|
|
209
|
+
return Array.isArray(spec) ? spec : [spec];
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Builds arguments for `element.animate(keyframes, options)`: two keyframes
|
|
213
|
+
* carrying the endpoint values, and options carrying the spring's `linear()`
|
|
214
|
+
* easing and duration. Pass an array of specs to drive several properties
|
|
215
|
+
* from one spring. A settling spring fills forwards. An undamped one repeats
|
|
216
|
+
* infinitely in alternating directions.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```ts
|
|
220
|
+
* const { keyframes, options } = springToWaapi(config, {
|
|
221
|
+
* property: 'translate',
|
|
222
|
+
* from: 0,
|
|
223
|
+
* to: 300,
|
|
224
|
+
* unit: 'px',
|
|
225
|
+
* })
|
|
226
|
+
* element.animate(keyframes, options)
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
function springToWaapi(config, spec, options = {}) {
|
|
230
|
+
const { easing, from, to } = resolveSpecs(config, asArray(spec), options);
|
|
231
|
+
const keyframes = [from, to];
|
|
232
|
+
const shared = {
|
|
233
|
+
duration: easing.duration,
|
|
234
|
+
easing: easing.easing
|
|
235
|
+
};
|
|
236
|
+
return {
|
|
237
|
+
keyframes,
|
|
238
|
+
options: easing.mode === "loop" ? {
|
|
239
|
+
...shared,
|
|
240
|
+
iterations: Number.POSITIVE_INFINITY,
|
|
241
|
+
direction: "alternate"
|
|
242
|
+
} : {
|
|
243
|
+
...shared,
|
|
244
|
+
fill: "forwards"
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Builds CSS text for the animation: a `@keyframes` rule named by `name`
|
|
250
|
+
* (default `'coily'`) and an `animation` shorthand value that references
|
|
251
|
+
* it, driving one property or several from one spring. A settling spring
|
|
252
|
+
* fills forwards. An undamped one repeats infinitely in alternating
|
|
253
|
+
* directions.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```ts
|
|
257
|
+
* const { keyframes, animation } = springToCss(config, {
|
|
258
|
+
* property: 'translate',
|
|
259
|
+
* from: 0,
|
|
260
|
+
* to: 300,
|
|
261
|
+
* unit: 'px',
|
|
262
|
+
* })
|
|
263
|
+
* const style = document.createElement('style')
|
|
264
|
+
* style.textContent = keyframes
|
|
265
|
+
* document.head.append(style)
|
|
266
|
+
* element.style.animation = animation
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
function springToCss(config, spec, options = {}) {
|
|
270
|
+
const { easing, from, to } = resolveSpecs(config, asArray(spec), options);
|
|
271
|
+
const name = options.name ?? "coily";
|
|
272
|
+
const ms = `${Number(easing.duration.toFixed(1))}ms`;
|
|
273
|
+
const declare = (values) => Object.entries(values).map(([property, value]) => `${property}: ${value};`).join(" ");
|
|
274
|
+
return {
|
|
275
|
+
keyframes: `@keyframes ${name} {\n from { ${declare(from)} }\n to { ${declare(to)} }\n}`,
|
|
276
|
+
animation: easing.mode === "loop" ? `${ms} ${easing.easing} infinite alternate ${name}` : `${ms} ${easing.easing} forwards ${name}`
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Builds a CSS `transition` value that animates a property change with the
|
|
281
|
+
* spring. Set it on the element, then change the property, for example on
|
|
282
|
+
* `:hover`, with a toggled class, or with a state attribute. Pass several
|
|
283
|
+
* specs to build one `transition` for several properties.
|
|
284
|
+
*
|
|
285
|
+
* The values come from your CSS rules. Each spec's `from` and `to` only
|
|
286
|
+
* size the move, which tunes the duration. An undamped (looping) config
|
|
287
|
+
* throws, because a transition cannot loop.
|
|
288
|
+
*
|
|
289
|
+
* A transition starts from rest unless its spec carries a `velocity`, in
|
|
290
|
+
* the property's units per second, such as a drag's speed at release. The
|
|
291
|
+
* velocity reshapes the curve and the duration, so build the transition at
|
|
292
|
+
* the handoff. A spec with a velocity must be the only spec in the call.
|
|
293
|
+
*
|
|
294
|
+
* ```ts
|
|
295
|
+
* // Release a dragged card back to 0 at the drag's speed.
|
|
296
|
+
* card.style.transition = springToTransition(config, {
|
|
297
|
+
* property: 'translate',
|
|
298
|
+
* from: dragX,
|
|
299
|
+
* to: 0,
|
|
300
|
+
* velocity: releaseVelocity,
|
|
301
|
+
* })
|
|
302
|
+
* card.style.translate = '0px'
|
|
303
|
+
* ```
|
|
304
|
+
*
|
|
305
|
+
* The browser carries no momentum between transitions, so a change that
|
|
306
|
+
* interrupts one mid-flight stalls at the turn. To keep the momentum, read
|
|
307
|
+
* the velocity with `springStateAt` and rebuild the transition with it.
|
|
308
|
+
* For motion that reverses often, drive a live `Spring` instead.
|
|
309
|
+
*/
|
|
310
|
+
function springToTransition(config, spec, options = {}) {
|
|
311
|
+
const specs = asArray(spec);
|
|
312
|
+
const { easing } = resolveSpecs(config, specs, options);
|
|
313
|
+
invariant(easing.mode === "settle", "springToTransition requires a settling spring; an undamped (looping) config has no single end state to transition to");
|
|
314
|
+
const ms = `${Number(easing.duration.toFixed(1))}ms`;
|
|
315
|
+
return specs.map((s) => `${s.property} ${ms} ${easing.easing}`).join(", ");
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
export { springFromState, springStateAt, springToCss, springToLinear, springToTransition, springToWaapi };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { i as defineSpring, n as SpringDefinitionOptions, r as SpringState, t as SpringDefinition } from "./config-B8M2xp26.js";
|
|
2
|
+
import { A as SpringSourceSymbol, C as accelerationOf, D as SourceValues, E as SourceShape, O as SpringSource, S as TargetedSourceApi, T as velocityOf, _ as Spring, a as createSpringSystem, b as KinematicSourceApi, c as CompositeSpring, d as PartialShape, f as PurposeShape, g as Purpose, h as TargetShape, i as SpringSystemOptions, j as mapSpring, k as SpringSourceApi, l as CompositeSpringTarget, m as Shape, n as SpringOptions, o as Ramp, p as ReadonlyShape, r as SpringSystem, s as RampOptions, t as CompositeSpringOptions, u as ConfigShape, v as SpringTarget, w as targetOf, x as TargetedSource, y as KinematicSource } from "./system-C7PbI4L0.js";
|
|
3
|
+
export { type CompositeSpring, type CompositeSpringOptions, type CompositeSpringTarget, type ConfigShape, type KinematicSource, type KinematicSourceApi, type PartialShape, type Purpose, type PurposeShape, type Ramp, type RampOptions, type ReadonlyShape, type Shape, type SourceShape, type SourceValues, type Spring, SpringDefinition, type SpringDefinitionOptions, type SpringOptions, type SpringSource, type SpringSourceApi, SpringSourceSymbol, type SpringState, type SpringSystem, type SpringSystemOptions, type SpringTarget, type TargetShape, type TargetedSource, type TargetedSourceApi, accelerationOf, createSpringSystem, defineSpring, mapSpring, targetOf, velocityOf };
|
package/dist/index.js
CHANGED
|
@@ -1,78 +1,124 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as SpringSourceSymbol, c as registerRecipe, i as defineSpring, o as isSpringSource, r as SpringDefinition, s as mapSpring, t as createSpringSystem } from "./system-WejqGkfv.js";
|
|
2
|
+
import { o as invariant } from "./solver-CAXuxdKj.js";
|
|
2
3
|
//#region src/kinematic-source.ts
|
|
3
4
|
function isKinematicSource(value) {
|
|
4
5
|
if (!isSpringSource(value)) return false;
|
|
5
6
|
const api = value[SpringSourceSymbol];
|
|
6
7
|
return "velocity" in api && "acceleration" in api;
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
+
function deriveView(source, read) {
|
|
10
|
+
const api = source[SpringSourceSymbol];
|
|
11
|
+
const view = Object.freeze({ [SpringSourceSymbol]: Object.freeze({
|
|
12
|
+
get value() {
|
|
13
|
+
return read();
|
|
14
|
+
},
|
|
15
|
+
onUpdate: (callback) => api.onUpdate(callback),
|
|
16
|
+
onDispose: (callback) => api.onDispose(callback)
|
|
17
|
+
}) });
|
|
18
|
+
registerRecipe(view, {
|
|
19
|
+
sources: [source],
|
|
20
|
+
read,
|
|
21
|
+
fns: []
|
|
22
|
+
});
|
|
23
|
+
return view;
|
|
24
|
+
}
|
|
25
|
+
const NOT_KINEMATIC = "expected a source in motion (a Spring, CompositeSpring, or Ramp); a derived source, such as a mapSpring result, has neither velocity nor acceleration";
|
|
9
26
|
/**
|
|
10
|
-
* Derives a source
|
|
11
|
-
* in value units per second.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* follow it (`blur.target = velocityOf(motion)`) and a map can shape it
|
|
27
|
+
* Derives a source that reads another source's velocity: the rate its
|
|
28
|
+
* value changes, in value units per second. Speed-driven effects then
|
|
29
|
+
* use the same tools as everything else. A spring can follow it
|
|
30
|
+
* (`blur.target = velocityOf(motion)`), and a map can shape it
|
|
15
31
|
* (`mapSpring(velocityOf(motion), (v) => 1 + Math.abs(v) * 0.001)` for
|
|
16
|
-
* squash and stretch). A scalar source yields a scalar velocity
|
|
17
|
-
*
|
|
18
|
-
*
|
|
32
|
+
* squash and stretch). A scalar source yields a scalar velocity. A
|
|
33
|
+
* `CompositeSpring` yields a velocity of its own shape, which you map to
|
|
34
|
+
* a scalar as you would its value.
|
|
19
35
|
*
|
|
20
|
-
* The result is a stateless view
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* target, as they would from a disposed spring.
|
|
36
|
+
* The result is a stateless view that holds no subscriptions and needs
|
|
37
|
+
* no disposal. Each read reports the source's current velocity. It
|
|
38
|
+
* updates when the source does and is released with it, and followers
|
|
39
|
+
* then detach and keep their current target.
|
|
25
40
|
*
|
|
26
|
-
* Only a
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* instead.
|
|
41
|
+
* Only a `Spring`, a `CompositeSpring`, a `Ramp`, or a Vue ref that wraps
|
|
42
|
+
* one qualifies. A `mapSpring` result has no velocity, and `velocityOf`
|
|
43
|
+
* rejects it at the type level and at runtime. Take `velocityOf` of the
|
|
44
|
+
* spring the map reads instead.
|
|
31
45
|
*/
|
|
32
46
|
function velocityOf(source) {
|
|
33
47
|
invariant(isKinematicSource(source), NOT_KINEMATIC);
|
|
34
48
|
const api = source[SpringSourceSymbol];
|
|
35
|
-
return
|
|
36
|
-
get value() {
|
|
37
|
-
return api.velocity;
|
|
38
|
-
},
|
|
39
|
-
onUpdate: (callback) => api.onUpdate(callback),
|
|
40
|
-
onDispose: (callback) => api.onDispose(callback)
|
|
41
|
-
}) });
|
|
49
|
+
return deriveView(source, () => api.velocity);
|
|
42
50
|
}
|
|
43
51
|
/**
|
|
44
|
-
* Derives a source
|
|
45
|
-
* changes, in value units per second squared.
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* it into an impact flash or a jelly wobble
|
|
52
|
+
* Derives a source that reads another source's acceleration: the rate
|
|
53
|
+
* its velocity changes, in value units per second squared. On a spring it
|
|
54
|
+
* is largest just after a retarget and near the far end of each swing,
|
|
55
|
+
* and passes through zero where a swing moves fastest. A spring can
|
|
56
|
+
* follow it, and a map can turn it into an impact flash or a jelly wobble
|
|
50
57
|
* (`mapSpring(accelerationOf(motion), (a) => Math.min(1, Math.abs(a) * 1e-4))`).
|
|
51
|
-
* A scalar source yields a scalar acceleration
|
|
52
|
-
*
|
|
53
|
-
*
|
|
58
|
+
* A scalar source yields a scalar acceleration. A `CompositeSpring`
|
|
59
|
+
* yields an acceleration of its own shape, which you map to a scalar as
|
|
60
|
+
* you would its value. A `Ramp`'s acceleration is always 0.
|
|
54
61
|
*
|
|
55
|
-
* The result is a stateless view
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* spring.
|
|
62
|
+
* The result is a stateless view that holds no subscriptions and needs
|
|
63
|
+
* no disposal. Each read reports the source's current acceleration. It
|
|
64
|
+
* updates when the source does and is released with it, and followers
|
|
65
|
+
* then detach and keep their current target.
|
|
60
66
|
*
|
|
61
|
-
* Only a
|
|
62
|
-
*
|
|
63
|
-
* at the type level and at runtime
|
|
64
|
-
* the map reads instead.
|
|
67
|
+
* Only a `Spring`, a `CompositeSpring`, a `Ramp`, or a Vue ref that wraps
|
|
68
|
+
* one qualifies. A `mapSpring` result has no acceleration, and
|
|
69
|
+
* `accelerationOf` rejects it at the type level and at runtime. Take
|
|
70
|
+
* `accelerationOf` of the spring the map reads instead.
|
|
65
71
|
*/
|
|
66
72
|
function accelerationOf(source) {
|
|
67
73
|
invariant(isKinematicSource(source), NOT_KINEMATIC);
|
|
68
74
|
const api = source[SpringSourceSymbol];
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
return deriveView(source, () => api.acceleration);
|
|
76
|
+
}
|
|
77
|
+
function isTargetedSource(value) {
|
|
78
|
+
return isKinematicSource(value) && "target" in value[SpringSourceSymbol];
|
|
79
|
+
}
|
|
80
|
+
const NO_TARGET = "expected a source with a target (a Spring, CompositeSpring, or Ramp); a derived source, such as a mapSpring result, has no target";
|
|
81
|
+
/**
|
|
82
|
+
* Derives a source that reads a spring's or ramp's target: where it is
|
|
83
|
+
* heading, not where it is. Pair it with the source in a `mapSpring`
|
|
84
|
+
* shape to measure against the destination, such as remaining travel,
|
|
85
|
+
* progress, or a lead that anticipates the move. A `Spring` or a bounded
|
|
86
|
+
* `Ramp` yields a scalar target that a spring can follow. An endless
|
|
87
|
+
* `Ramp` yields `Infinity` or `-Infinity`, and a spring that follows it
|
|
88
|
+
* throws. A `CompositeSpring` yields a target of its own shape, which you
|
|
89
|
+
* map to a scalar as you would its value.
|
|
90
|
+
*
|
|
91
|
+
* ```ts
|
|
92
|
+
* const system = createSpringSystem()
|
|
93
|
+
* const body = system.createSpring(0)
|
|
94
|
+
* // The eyes look ahead by the body's remaining travel, capped at 20.
|
|
95
|
+
* const lead = mapSpring({ value: body, target: targetOf(body) }, ({ value, target }) =>
|
|
96
|
+
* Math.max(-20, Math.min(20, target - value)),
|
|
97
|
+
* )
|
|
98
|
+
* const eyes = system.createSpring(lead)
|
|
99
|
+
*
|
|
100
|
+
* body.target = 100
|
|
101
|
+
* system.advance(100)
|
|
102
|
+
* eyes.target // 20: the body still has more than 20 to go
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* The result is a stateless view that holds no subscriptions and needs
|
|
106
|
+
* no disposal. Each read reports the spring's current target. It updates
|
|
107
|
+
* when the spring does, but assigning `target` emits nothing by itself,
|
|
108
|
+
* so followers pick up a new target on the next frame, when the spring
|
|
109
|
+
* starts toward it. Under reduced motion, a `'motion'` spring jumps to
|
|
110
|
+
* its new target and notifies followers at once. The view is released
|
|
111
|
+
* with the spring, and followers then detach and keep their current
|
|
112
|
+
* target.
|
|
113
|
+
*
|
|
114
|
+
* Only a `Spring`, a `CompositeSpring`, a `Ramp`, or a Vue ref that wraps
|
|
115
|
+
* one has a target. `targetOf` rejects a `mapSpring` result at the type
|
|
116
|
+
* level and at runtime.
|
|
117
|
+
*/
|
|
118
|
+
function targetOf(spring) {
|
|
119
|
+
invariant(isTargetedSource(spring), NO_TARGET);
|
|
120
|
+
const api = spring[SpringSourceSymbol];
|
|
121
|
+
return deriveView(spring, () => api.target);
|
|
76
122
|
}
|
|
77
123
|
//#endregion
|
|
78
|
-
export { SpringSourceSymbol, accelerationOf, createSpringSystem, defineSpring, mapSpring, velocityOf };
|
|
124
|
+
export { SpringDefinition, SpringSourceSymbol, accelerationOf, createSpringSystem, defineSpring, mapSpring, targetOf, velocityOf };
|
package/dist/nuxt.d.ts
CHANGED
|
@@ -11,39 +11,48 @@ interface CoilyModuleOptions {
|
|
|
11
11
|
*/
|
|
12
12
|
debug?: boolean;
|
|
13
13
|
/**
|
|
14
|
-
* Frame-rate cap. 0 means uncapped: one tick per displayed frame.
|
|
15
|
-
*
|
|
16
|
-
* elapsed time.
|
|
14
|
+
* Frame-rate cap. 0 means uncapped: one tick per displayed frame. A
|
|
15
|
+
* capped tick lands on a whole display frame and receives the true
|
|
16
|
+
* elapsed time in ms.
|
|
17
17
|
* @default 0
|
|
18
18
|
*/
|
|
19
19
|
fps?: number;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* resume. 0
|
|
21
|
+
* The frame gap in ms above which the system treats the gap as lag and
|
|
22
|
+
* replaces it with `adjustedLag`, so springs do not jump when frames
|
|
23
|
+
* resume. 0 turns off lag clamping.
|
|
24
24
|
* @default 500
|
|
25
25
|
*/
|
|
26
26
|
lagThreshold?: number;
|
|
27
27
|
/**
|
|
28
|
-
* The elapsed
|
|
29
|
-
*
|
|
28
|
+
* The elapsed time in ms that replaces a lagging frame. A frame uses
|
|
29
|
+
* at most `lagThreshold` ms of it.
|
|
30
30
|
* @default 33
|
|
31
31
|
*/
|
|
32
32
|
adjustedLag?: number;
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* How far a spring can trail a source it follows within one frame, in
|
|
35
|
+
* the value's own units. Frames whose motion would exceed it run as
|
|
36
|
+
* sub-steps, so a smaller value follows more closely at more cost.
|
|
37
|
+
* @default 0.1
|
|
38
|
+
*/
|
|
39
|
+
couplingTolerance?: number;
|
|
40
|
+
/**
|
|
41
|
+
* When springs snap to their targets instead of animating. `'user'`
|
|
35
42
|
* follows the OS prefers-reduced-motion setting, including live
|
|
36
|
-
* changes
|
|
43
|
+
* changes. `'always'` and `'never'` force one behavior.
|
|
37
44
|
* @default 'user'
|
|
38
45
|
*/
|
|
39
46
|
reducedMotion?: 'user' | 'always' | 'never';
|
|
40
47
|
}
|
|
41
48
|
/**
|
|
42
|
-
* The Nuxt module
|
|
43
|
-
* client
|
|
44
|
-
*
|
|
45
|
-
* `
|
|
46
|
-
*
|
|
49
|
+
* The Nuxt module. It creates one spring system for the app and starts
|
|
50
|
+
* it on the client only, so server rendering shows springs at their
|
|
51
|
+
* initial values. The module auto-imports
|
|
52
|
+
* `useSpring`, `useRamp`, `useSpringSystem`, `useSpringPool`,
|
|
53
|
+
* `defineSpring`, `mapSpring`, `velocityOf`, `accelerationOf`, and
|
|
54
|
+
* `targetOf`, and registers the `SpringValue` component. It does not
|
|
55
|
+
* auto-import types.
|
|
47
56
|
*/
|
|
48
57
|
declare const coilyModule: NuxtModule;
|
|
49
58
|
//#endregion
|
package/dist/nuxt.js
CHANGED
|
@@ -6,6 +6,7 @@ function getPluginTemplate(options) {
|
|
|
6
6
|
if (options.fps !== void 0) systemOptions.fps = options.fps;
|
|
7
7
|
if (options.lagThreshold !== void 0) systemOptions.lagThreshold = options.lagThreshold;
|
|
8
8
|
if (options.adjustedLag !== void 0) systemOptions.adjustedLag = options.adjustedLag;
|
|
9
|
+
if (options.couplingTolerance !== void 0) systemOptions.couplingTolerance = options.couplingTolerance;
|
|
9
10
|
if (options.reducedMotion) systemOptions.reducedMotion = options.reducedMotion;
|
|
10
11
|
return `import { createSpringSystem } from 'coily'
|
|
11
12
|
import { provideSpringSystem } from 'coily/vue'
|
|
@@ -22,11 +23,13 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
22
23
|
})`;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
|
-
* The Nuxt module
|
|
26
|
-
* client
|
|
27
|
-
*
|
|
28
|
-
* `
|
|
29
|
-
*
|
|
26
|
+
* The Nuxt module. It creates one spring system for the app and starts
|
|
27
|
+
* it on the client only, so server rendering shows springs at their
|
|
28
|
+
* initial values. The module auto-imports
|
|
29
|
+
* `useSpring`, `useRamp`, `useSpringSystem`, `useSpringPool`,
|
|
30
|
+
* `defineSpring`, `mapSpring`, `velocityOf`, `accelerationOf`, and
|
|
31
|
+
* `targetOf`, and registers the `SpringValue` component. It does not
|
|
32
|
+
* auto-import types.
|
|
30
33
|
*/
|
|
31
34
|
const coilyModule = defineNuxtModule({
|
|
32
35
|
meta: {
|
|
@@ -46,6 +49,10 @@ const coilyModule = defineNuxtModule({
|
|
|
46
49
|
from: "coily/vue",
|
|
47
50
|
name: "useSpring"
|
|
48
51
|
},
|
|
52
|
+
{
|
|
53
|
+
from: "coily/vue",
|
|
54
|
+
name: "useRamp"
|
|
55
|
+
},
|
|
49
56
|
{
|
|
50
57
|
from: "coily/vue",
|
|
51
58
|
name: "useSpringSystem"
|
|
@@ -69,6 +76,10 @@ const coilyModule = defineNuxtModule({
|
|
|
69
76
|
{
|
|
70
77
|
from: "coily",
|
|
71
78
|
name: "accelerationOf"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
from: "coily",
|
|
82
|
+
name: "targetOf"
|
|
72
83
|
}
|
|
73
84
|
]);
|
|
74
85
|
addComponent({
|