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/dist/css.d.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { r as SpringState, t as SpringDefinition } from "./config-OOMMRdK8.js";
|
|
2
|
+
//#region src/css.d.ts
|
|
3
|
+
interface LinearEasingOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Animation range the duration is tuned for, in value units. The easing
|
|
6
|
+
* shape is identical regardless; only the settle time scales with it.
|
|
7
|
+
* The keyframe helpers (`springToWaapi`, `springToCss`,
|
|
8
|
+
* `springToTransition`) default it to the largest move among their
|
|
9
|
+
* specs instead.
|
|
10
|
+
* @default 1
|
|
11
|
+
*/
|
|
12
|
+
readonly displacement?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Max output error of the piecewise-linear approximation, in progress
|
|
15
|
+
* units (0..1). Smaller keeps more stops.
|
|
16
|
+
* @default 0.0025
|
|
17
|
+
*/
|
|
18
|
+
readonly maxError?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Hard cap on generated duration, in ms, for near-undamped configs.
|
|
21
|
+
* @default 10000
|
|
22
|
+
*/
|
|
23
|
+
readonly maxDuration?: number;
|
|
24
|
+
}
|
|
25
|
+
interface EasingBase {
|
|
26
|
+
/** A CSS `linear()` easing string. */
|
|
27
|
+
readonly easing: string;
|
|
28
|
+
/** Duration to pair the easing with, in ms. */
|
|
29
|
+
readonly duration: number;
|
|
30
|
+
/** Number of stops in the easing (diagnostic). */
|
|
31
|
+
readonly stops: number;
|
|
32
|
+
/** Worst reconstruction error vs the dense trajectory (diagnostic). */
|
|
33
|
+
readonly maxError: number;
|
|
34
|
+
}
|
|
35
|
+
/** The easing of a spring that comes to rest: play it once over `duration`. */
|
|
36
|
+
interface SettlingEasing extends EasingBase {
|
|
37
|
+
readonly mode: 'settle';
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The easing of a spring that never rests — an undamped oscillation,
|
|
41
|
+
* expressed as a seamless loop. Animate the property between its two
|
|
42
|
+
* swing extremes (`from` and `2 * target - from`, target at the 50%
|
|
43
|
+
* midpoint) with `animation-iteration-count: infinite` and
|
|
44
|
+
* `animation-direction: alternate`. `duration` is one half period.
|
|
45
|
+
*/
|
|
46
|
+
interface LoopingEasing extends EasingBase {
|
|
47
|
+
readonly mode: 'loop';
|
|
48
|
+
readonly direction: 'alternate';
|
|
49
|
+
readonly iterations: 'infinite';
|
|
50
|
+
}
|
|
51
|
+
type LinearEasing = SettlingEasing | LoopingEasing;
|
|
52
|
+
/**
|
|
53
|
+
* Turns a resolved spring config into a CSS `linear()` easing and the
|
|
54
|
+
* duration in ms to pair it with. A spring that settles returns a
|
|
55
|
+
* `'settle'` easing. A pure undamped config (`dampingRatio: 0` with
|
|
56
|
+
* passthrough `arrival`) has no friction and never settles, so it
|
|
57
|
+
* returns a `'loop'` easing for an infinite alternating animation.
|
|
58
|
+
*/
|
|
59
|
+
declare function springToLinear(config: SpringDefinition, options?: LinearEasingOptions): LinearEasing;
|
|
60
|
+
/**
|
|
61
|
+
* Like `springToLinear`, but from a spring caught mid-flight rather than at
|
|
62
|
+
* rest: `state` carries both the displacement from the target (`position`)
|
|
63
|
+
* and the current `velocity`, so the easing continues the motion smoothly
|
|
64
|
+
* instead of restarting. Use it to regenerate an animation when its target
|
|
65
|
+
* changes — the interruption carries momentum instead of stalling. The
|
|
66
|
+
* from-rest shape trick doesn't apply here (the curve now depends on the
|
|
67
|
+
* velocity), so this is computed per interruption, not cached per config.
|
|
68
|
+
*
|
|
69
|
+
* Always a settling easing; `position` must be nonzero.
|
|
70
|
+
*/
|
|
71
|
+
declare function springFromState(config: SpringDefinition, state: SpringState, options?: LinearEasingOptions): LinearEasing;
|
|
72
|
+
/**
|
|
73
|
+
* Evolves a spring `state` forward by `elapsedMs`, returning the new
|
|
74
|
+
* displacement and velocity. Pair it with a running `element.animate` — read
|
|
75
|
+
* the animation's `currentTime`, evolve the state it started from, and you
|
|
76
|
+
* have the exact live velocity the Web Animations API never exposes, ready
|
|
77
|
+
* to hand to `springFromState` on interruption.
|
|
78
|
+
*/
|
|
79
|
+
declare function springStateAt(config: SpringDefinition, state: SpringState, elapsedMs: number): SpringState;
|
|
80
|
+
/** What to animate: a CSS property from one numeric value to another. */
|
|
81
|
+
interface PropertySpec {
|
|
82
|
+
/** The CSS property, e.g. `'opacity'`, `'transform'`, `'translate'`. */
|
|
83
|
+
readonly property: string;
|
|
84
|
+
/** Start value. */
|
|
85
|
+
readonly from: number;
|
|
86
|
+
/** Target value the spring is tuned toward. */
|
|
87
|
+
readonly to: number;
|
|
88
|
+
/**
|
|
89
|
+
* Unit appended to numbers, e.g. `'px'`, `'%'`, `'deg'`.
|
|
90
|
+
* @default ''
|
|
91
|
+
*/
|
|
92
|
+
readonly unit?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Formats an interpolated number into a property value, overriding
|
|
95
|
+
* `unit` — needed for wrapped values like `(v) => `translateX(${v}px)``.
|
|
96
|
+
*/
|
|
97
|
+
readonly format?: (value: number) => string;
|
|
98
|
+
/**
|
|
99
|
+
* The property's current velocity, in its units per second. Set it to
|
|
100
|
+
* regenerate an interrupted animation from its live motion so it carries
|
|
101
|
+
* momentum instead of restarting from rest. Single-property only — a
|
|
102
|
+
* from-state easing depends on that property's velocity and can't be
|
|
103
|
+
* shared across properties.
|
|
104
|
+
*/
|
|
105
|
+
readonly velocity?: number;
|
|
106
|
+
}
|
|
107
|
+
interface CssOptions extends LinearEasingOptions {
|
|
108
|
+
/**
|
|
109
|
+
* Name for the generated `@keyframes` rule.
|
|
110
|
+
* @default 'coily'
|
|
111
|
+
*/
|
|
112
|
+
readonly name?: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Builds arguments for `element.animate(keyframes, options)`: two keyframes
|
|
116
|
+
* carrying the endpoint values, and options carrying the spring's `linear()`
|
|
117
|
+
* easing and duration. Pass an array of specs to drive several properties
|
|
118
|
+
* from one spring. A settling spring fills forwards; an undamped one repeats
|
|
119
|
+
* infinitely in alternating directions.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const { keyframes, options } = springToWaapi(config, {
|
|
124
|
+
* property: 'translate',
|
|
125
|
+
* from: 0,
|
|
126
|
+
* to: 300,
|
|
127
|
+
* unit: 'px',
|
|
128
|
+
* })
|
|
129
|
+
* element.animate(keyframes, options)
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare function springToWaapi(config: SpringDefinition, spec: PropertySpec | PropertySpec[], options?: LinearEasingOptions): {
|
|
133
|
+
keyframes: Keyframe[];
|
|
134
|
+
options: KeyframeAnimationOptions;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Builds CSS text for the animation: a `@keyframes` rule and an `animation`
|
|
138
|
+
* shorthand value, driving one property or several from one spring. The
|
|
139
|
+
* `name` option (default `'coily'`) names the `@keyframes` rule and is
|
|
140
|
+
* referenced by the `animation` value.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* const { keyframes, animation } = springToCss(config, {
|
|
145
|
+
* property: 'translate',
|
|
146
|
+
* from: 0,
|
|
147
|
+
* to: 300,
|
|
148
|
+
* unit: 'px',
|
|
149
|
+
* })
|
|
150
|
+
* const style = document.createElement('style')
|
|
151
|
+
* style.textContent = keyframes
|
|
152
|
+
* document.head.append(style)
|
|
153
|
+
* element.style.animation = animation
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
declare function springToCss(config: SpringDefinition, spec: PropertySpec | PropertySpec[], options?: CssOptions): {
|
|
157
|
+
keyframes: string;
|
|
158
|
+
animation: string;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Builds a CSS `transition` value that springs a property change: set it on
|
|
162
|
+
* the element, then change the property (on `:hover`, a toggled class, a
|
|
163
|
+
* state attribute) to animate. Pass several specs for one `transition`
|
|
164
|
+
* across properties.
|
|
165
|
+
*
|
|
166
|
+
* Only settling springs — a transition can't loop. It also can't carry
|
|
167
|
+
* momentum: a change interrupted mid-flight restarts the spring from rest
|
|
168
|
+
* instead of reversing through its current velocity, so it stalls at the
|
|
169
|
+
* turn. For motion that's reversed often, drive a live `Spring` instead. The
|
|
170
|
+
* specs' `from`/`to` only tune the duration; the actual values live in your
|
|
171
|
+
* CSS rules.
|
|
172
|
+
*/
|
|
173
|
+
declare function springToTransition(config: SpringDefinition, spec: PropertySpec | PropertySpec[], options?: LinearEasingOptions): string;
|
|
174
|
+
//#endregion
|
|
175
|
+
export { CssOptions, LinearEasing, LinearEasingOptions, LoopingEasing, PropertySpec, SettlingEasing, springFromState, springStateAt, springToCss, springToLinear, springToTransition, springToWaapi };
|
package/dist/css.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { i as State, n as OverdampedSolver, o as invariant, r as UnderdampedSolver, t as CriticallyDampedSolver } from "./solver-CVXOTDkZ.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 resolved spring config into a CSS `linear()` easing and the
|
|
123
|
+
* duration in ms to pair it with. A spring that settles returns a
|
|
124
|
+
* `'settle'` easing. A pure undamped config (`dampingRatio: 0` with
|
|
125
|
+
* passthrough `arrival`) has no friction and never settles, so it
|
|
126
|
+
* returns a `'loop'` easing for an infinite alternating animation.
|
|
127
|
+
*/
|
|
128
|
+
function springToLinear(config, options = {}) {
|
|
129
|
+
const displacement = options.displacement ?? 1;
|
|
130
|
+
const maxError = options.maxError ?? .0025;
|
|
131
|
+
const maxDuration = options.maxDuration ?? 1e4;
|
|
132
|
+
if (config.dampingRatio === 0 && config.arrival === 1) return buildLoop(config, displacement, maxError);
|
|
133
|
+
const settleTime = config.computeTimeRemaining({
|
|
134
|
+
position: displacement,
|
|
135
|
+
velocity: 0
|
|
136
|
+
});
|
|
137
|
+
return buildSettle(config, displacement, Math.min(settleTime, maxDuration), maxError);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Like `springToLinear`, but from a spring caught mid-flight rather than at
|
|
141
|
+
* rest: `state` carries both the displacement from the target (`position`)
|
|
142
|
+
* and the current `velocity`, so the easing continues the motion smoothly
|
|
143
|
+
* instead of restarting. Use it to regenerate an animation when its target
|
|
144
|
+
* changes — the interruption carries momentum instead of stalling. The
|
|
145
|
+
* from-rest shape trick doesn't apply here (the curve now depends on the
|
|
146
|
+
* velocity), so this is computed per interruption, not cached per config.
|
|
147
|
+
*
|
|
148
|
+
* Always a settling easing; `position` must be nonzero.
|
|
149
|
+
*/
|
|
150
|
+
function springFromState(config, state, options = {}) {
|
|
151
|
+
const maxError = options.maxError ?? .0025;
|
|
152
|
+
const maxDuration = options.maxDuration ?? 1e4;
|
|
153
|
+
invariant(Math.abs(state.position) > 1e-9, "springFromState needs a nonzero position (the value and target must differ)");
|
|
154
|
+
const settleTime = config.computeTimeRemaining(state);
|
|
155
|
+
return buildSettle(config, state.position, Math.min(settleTime, maxDuration), maxError, state.velocity);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Evolves a spring `state` forward by `elapsedMs`, returning the new
|
|
159
|
+
* displacement and velocity. Pair it with a running `element.animate` — read
|
|
160
|
+
* the animation's `currentTime`, evolve the state it started from, and you
|
|
161
|
+
* have the exact live velocity the Web Animations API never exposes, ready
|
|
162
|
+
* to hand to `springFromState` on interruption.
|
|
163
|
+
*/
|
|
164
|
+
function springStateAt(config, state, elapsedMs) {
|
|
165
|
+
if (elapsedMs <= 0) return {
|
|
166
|
+
position: state.position,
|
|
167
|
+
velocity: state.velocity
|
|
168
|
+
};
|
|
169
|
+
const live = new State(config, state.position, state.velocity);
|
|
170
|
+
pickSolver(config, live).tick(elapsedMs / 1e3);
|
|
171
|
+
return {
|
|
172
|
+
position: live.position,
|
|
173
|
+
velocity: live.velocity
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function resolveSpecs(config, specs, options) {
|
|
177
|
+
const withVelocity = specs.some((spec) => (spec.velocity ?? 0) !== 0);
|
|
178
|
+
invariant(!withVelocity || specs.length === 1, "velocity handoff is single-property: a from-state easing depends on that property, so run one animation per property");
|
|
179
|
+
const first = specs[0];
|
|
180
|
+
const easing = withVelocity ? springFromState(config, {
|
|
181
|
+
position: first.from - first.to,
|
|
182
|
+
velocity: first.velocity
|
|
183
|
+
}, options) : springToLinear(config, {
|
|
184
|
+
...options,
|
|
185
|
+
displacement: options.displacement ?? (Math.max(...specs.map((s) => Math.abs(s.to - s.from))) || 1)
|
|
186
|
+
});
|
|
187
|
+
const from = {};
|
|
188
|
+
const to = {};
|
|
189
|
+
for (const spec of specs) {
|
|
190
|
+
const format = spec.format ?? ((value) => `${Number(value.toFixed(3))}${spec.unit ?? ""}`);
|
|
191
|
+
const far = easing.mode === "loop" ? 2 * spec.to - spec.from : spec.to;
|
|
192
|
+
const start = format(spec.from);
|
|
193
|
+
const end = format(far);
|
|
194
|
+
from[spec.property] = from[spec.property] ? `${from[spec.property]} ${start}` : start;
|
|
195
|
+
to[spec.property] = to[spec.property] ? `${to[spec.property]} ${end}` : end;
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
easing,
|
|
199
|
+
from,
|
|
200
|
+
to
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function asArray(spec) {
|
|
204
|
+
return Array.isArray(spec) ? spec : [spec];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Builds arguments for `element.animate(keyframes, options)`: two keyframes
|
|
208
|
+
* carrying the endpoint values, and options carrying the spring's `linear()`
|
|
209
|
+
* easing and duration. Pass an array of specs to drive several properties
|
|
210
|
+
* from one spring. A settling spring fills forwards; an undamped one repeats
|
|
211
|
+
* infinitely in alternating directions.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* const { keyframes, options } = springToWaapi(config, {
|
|
216
|
+
* property: 'translate',
|
|
217
|
+
* from: 0,
|
|
218
|
+
* to: 300,
|
|
219
|
+
* unit: 'px',
|
|
220
|
+
* })
|
|
221
|
+
* element.animate(keyframes, options)
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
function springToWaapi(config, spec, options = {}) {
|
|
225
|
+
const { easing, from, to } = resolveSpecs(config, asArray(spec), options);
|
|
226
|
+
const keyframes = [from, to];
|
|
227
|
+
const shared = {
|
|
228
|
+
duration: easing.duration,
|
|
229
|
+
easing: easing.easing
|
|
230
|
+
};
|
|
231
|
+
return {
|
|
232
|
+
keyframes,
|
|
233
|
+
options: easing.mode === "loop" ? {
|
|
234
|
+
...shared,
|
|
235
|
+
iterations: Number.POSITIVE_INFINITY,
|
|
236
|
+
direction: "alternate"
|
|
237
|
+
} : {
|
|
238
|
+
...shared,
|
|
239
|
+
fill: "forwards"
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Builds CSS text for the animation: a `@keyframes` rule and an `animation`
|
|
245
|
+
* shorthand value, driving one property or several from one spring. The
|
|
246
|
+
* `name` option (default `'coily'`) names the `@keyframes` rule and is
|
|
247
|
+
* referenced by the `animation` value.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* const { keyframes, animation } = springToCss(config, {
|
|
252
|
+
* property: 'translate',
|
|
253
|
+
* from: 0,
|
|
254
|
+
* to: 300,
|
|
255
|
+
* unit: 'px',
|
|
256
|
+
* })
|
|
257
|
+
* const style = document.createElement('style')
|
|
258
|
+
* style.textContent = keyframes
|
|
259
|
+
* document.head.append(style)
|
|
260
|
+
* element.style.animation = animation
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
function springToCss(config, spec, options = {}) {
|
|
264
|
+
const { easing, from, to } = resolveSpecs(config, asArray(spec), options);
|
|
265
|
+
const name = options.name ?? "coily";
|
|
266
|
+
const ms = `${Number(easing.duration.toFixed(1))}ms`;
|
|
267
|
+
const declare = (values) => Object.entries(values).map(([property, value]) => `${property}: ${value};`).join(" ");
|
|
268
|
+
return {
|
|
269
|
+
keyframes: `@keyframes ${name} {\n from { ${declare(from)} }\n to { ${declare(to)} }\n}`,
|
|
270
|
+
animation: easing.mode === "loop" ? `${ms} ${easing.easing} infinite alternate ${name}` : `${ms} ${easing.easing} forwards ${name}`
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Builds a CSS `transition` value that springs a property change: set it on
|
|
275
|
+
* the element, then change the property (on `:hover`, a toggled class, a
|
|
276
|
+
* state attribute) to animate. Pass several specs for one `transition`
|
|
277
|
+
* across properties.
|
|
278
|
+
*
|
|
279
|
+
* Only settling springs — a transition can't loop. It also can't carry
|
|
280
|
+
* momentum: a change interrupted mid-flight restarts the spring from rest
|
|
281
|
+
* instead of reversing through its current velocity, so it stalls at the
|
|
282
|
+
* turn. For motion that's reversed often, drive a live `Spring` instead. The
|
|
283
|
+
* specs' `from`/`to` only tune the duration; the actual values live in your
|
|
284
|
+
* CSS rules.
|
|
285
|
+
*/
|
|
286
|
+
function springToTransition(config, spec, options = {}) {
|
|
287
|
+
const specs = asArray(spec);
|
|
288
|
+
const { easing } = resolveSpecs(config, specs, options);
|
|
289
|
+
invariant(easing.mode === "settle", "springToTransition requires a settling spring; an undamped (looping) config has no single end state to transition to");
|
|
290
|
+
const ms = `${Number(easing.duration.toFixed(1))}ms`;
|
|
291
|
+
return specs.map((s) => `${s.property} ${ms} ${easing.easing}`).join(", ");
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
294
|
+
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-OOMMRdK8.js";
|
|
2
|
+
import { _ as accelerationOf, a as createSpringSystem, b as SpringSourceSymbol, c as ConfigShape, g as KinematicSource, h as SpringTarget, i as SpringSystemOptions, m as Spring, n as SpringOptions, o as CompositeSpring, p as Purpose, r as SpringSystem, s as CompositeSpringTarget, t as CompositeSpringOptions, u as PurposeShape, v as velocityOf, x as mapSpring, y as SpringSource } from "./system-Du_fXXmO.js";
|
|
3
|
+
export { type CompositeSpring, type CompositeSpringOptions, type CompositeSpringTarget, type ConfigShape, type KinematicSource, type Purpose, type PurposeShape, type Spring, type SpringDefinition, type SpringDefinitionOptions, type SpringOptions, type SpringSource, SpringSourceSymbol, type SpringState, type SpringSystem, type SpringSystemOptions, type SpringTarget, accelerationOf, createSpringSystem, defineSpring, mapSpring, velocityOf };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,91 @@
|
|
|
1
|
-
import { r as defineSpring, t as createSpringSystem } from "./system-
|
|
2
|
-
|
|
1
|
+
import { a as registerRecipe, i as mapSpring, n as SpringSourceSymbol, r as isSpringSource, s as defineSpring, t as createSpringSystem } from "./system-Br_cPnaN.js";
|
|
2
|
+
import { o as invariant } from "./solver-CVXOTDkZ.js";
|
|
3
|
+
//#region src/kinematic-source.ts
|
|
4
|
+
function isKinematicSource(value) {
|
|
5
|
+
if (!isSpringSource(value)) return false;
|
|
6
|
+
const api = value[SpringSourceSymbol];
|
|
7
|
+
return "velocity" in api && "acceleration" in api;
|
|
8
|
+
}
|
|
9
|
+
const NOT_KINEMATIC = "expected a source in motion (a Spring or CompositeSpring); a value derived with mapSpring has neither velocity nor acceleration";
|
|
10
|
+
/**
|
|
11
|
+
* Derives a source from another's velocity — the rate its value changes,
|
|
12
|
+
* in value units per second. `velocityOf(motion)` is a source that reads
|
|
13
|
+
* `motion`'s current velocity, so speed-driven effects fall out of the
|
|
14
|
+
* same follow and `mapSpring` machinery as everything else: a spring can
|
|
15
|
+
* follow it (`blur.target = velocityOf(motion)`) and a map can shape it
|
|
16
|
+
* (`mapSpring(velocityOf(motion), (v) => 1 + Math.abs(v) * 0.001)` for
|
|
17
|
+
* squash and stretch). A scalar source yields a scalar velocity a spring
|
|
18
|
+
* can follow; a `CompositeSpring` or shape yields a velocity of the same
|
|
19
|
+
* shape, mapped to a scalar the way the composite itself is.
|
|
20
|
+
*
|
|
21
|
+
* The result is a stateless view, not a spring: it holds no
|
|
22
|
+
* subscriptions and needs no disposal, and reads report the source's
|
|
23
|
+
* current velocity on the fly. It updates when the source does and is
|
|
24
|
+
* released with it — followers detach then, keeping their current
|
|
25
|
+
* target, as they would from a disposed spring.
|
|
26
|
+
*
|
|
27
|
+
* Only a source in motion qualifies — a `Spring` or a `CompositeSpring`.
|
|
28
|
+
* A value derived with `mapSpring` has no velocity (its rate of change
|
|
29
|
+
* would need the derivative of the map), so it is rejected at the type
|
|
30
|
+
* level and at runtime; take `velocityOf` of the spring the map reads
|
|
31
|
+
* instead.
|
|
32
|
+
*/
|
|
33
|
+
function velocityOf(source) {
|
|
34
|
+
invariant(isKinematicSource(source), NOT_KINEMATIC);
|
|
35
|
+
const api = source[SpringSourceSymbol];
|
|
36
|
+
const velocity = Object.freeze({ [SpringSourceSymbol]: Object.freeze({
|
|
37
|
+
get value() {
|
|
38
|
+
return api.velocity;
|
|
39
|
+
},
|
|
40
|
+
onUpdate: (callback) => api.onUpdate(callback),
|
|
41
|
+
onDispose: (callback) => api.onDispose(callback)
|
|
42
|
+
}) });
|
|
43
|
+
registerRecipe(velocity, {
|
|
44
|
+
sources: [source],
|
|
45
|
+
read: () => api.velocity,
|
|
46
|
+
fns: []
|
|
47
|
+
});
|
|
48
|
+
return velocity;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Derives a source from another's acceleration — the rate its velocity
|
|
52
|
+
* changes, in value units per second squared. `accelerationOf(motion)`
|
|
53
|
+
* spikes when `motion` is pushed hardest and passes through zero at the
|
|
54
|
+
* peak of a swing, so force-driven effects fall out of the same follow
|
|
55
|
+
* and `mapSpring` machinery: a spring can follow it, and a map can turn
|
|
56
|
+
* it into an impact flash or a jelly wobble
|
|
57
|
+
* (`mapSpring(accelerationOf(motion), (a) => Math.min(1, Math.abs(a) * 1e-4))`).
|
|
58
|
+
* A scalar source yields a scalar acceleration; a `CompositeSpring` or
|
|
59
|
+
* shape yields an acceleration of the same shape, mapped to a scalar the
|
|
60
|
+
* way the composite itself is.
|
|
61
|
+
*
|
|
62
|
+
* The result is a stateless view, not a spring: it holds no
|
|
63
|
+
* subscriptions and needs no disposal, and reads report the source's
|
|
64
|
+
* current acceleration on the fly. It updates when the source does and
|
|
65
|
+
* is released with it, followers detaching then as from a disposed
|
|
66
|
+
* spring.
|
|
67
|
+
*
|
|
68
|
+
* Only a source in motion qualifies — a `Spring` or a `CompositeSpring`.
|
|
69
|
+
* A value derived with `mapSpring` has no acceleration, so it is rejected
|
|
70
|
+
* at the type level and at runtime; take `accelerationOf` of the spring
|
|
71
|
+
* the map reads instead.
|
|
72
|
+
*/
|
|
73
|
+
function accelerationOf(source) {
|
|
74
|
+
invariant(isKinematicSource(source), NOT_KINEMATIC);
|
|
75
|
+
const api = source[SpringSourceSymbol];
|
|
76
|
+
const acceleration = Object.freeze({ [SpringSourceSymbol]: Object.freeze({
|
|
77
|
+
get value() {
|
|
78
|
+
return api.acceleration;
|
|
79
|
+
},
|
|
80
|
+
onUpdate: (callback) => api.onUpdate(callback),
|
|
81
|
+
onDispose: (callback) => api.onDispose(callback)
|
|
82
|
+
}) });
|
|
83
|
+
registerRecipe(acceleration, {
|
|
84
|
+
sources: [source],
|
|
85
|
+
read: () => api.acceleration,
|
|
86
|
+
fns: []
|
|
87
|
+
});
|
|
88
|
+
return acceleration;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { SpringSourceSymbol, accelerationOf, createSpringSystem, defineSpring, mapSpring, velocityOf };
|
package/dist/nuxt.d.ts
CHANGED
|
@@ -1,12 +1,58 @@
|
|
|
1
1
|
import { NuxtModule } from "@nuxt/schema";
|
|
2
2
|
//#region src/vue/nuxt.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Options for the `coily` key in `nuxt.config`, passed to the app-wide
|
|
5
|
+
* spring system the module creates.
|
|
6
|
+
*/
|
|
3
7
|
interface CoilyModuleOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Log active motion counts to the console whenever they change.
|
|
10
|
+
* @default false
|
|
11
|
+
*/
|
|
4
12
|
debug?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Frame-rate cap. 0 means uncapped: one tick per displayed frame.
|
|
15
|
+
* Capped ticks land on whole display frames and receive the true
|
|
16
|
+
* elapsed time.
|
|
17
|
+
* @default 0
|
|
18
|
+
*/
|
|
5
19
|
fps?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Frame gap in milliseconds above which the gap is treated as lag and
|
|
22
|
+
* replaced with `adjustedLag`, so springs don't teleport when frames
|
|
23
|
+
* resume. 0 disables lag clamping.
|
|
24
|
+
* @default 500
|
|
25
|
+
*/
|
|
6
26
|
lagThreshold?: number;
|
|
27
|
+
/**
|
|
28
|
+
* The elapsed milliseconds a lagging frame is replaced with. Clamped
|
|
29
|
+
* to at most `lagThreshold`.
|
|
30
|
+
* @default 33
|
|
31
|
+
*/
|
|
7
32
|
adjustedLag?: number;
|
|
33
|
+
/**
|
|
34
|
+
* How far a spring may trail a source it follows within one frame, in
|
|
35
|
+
* the value's own units. Frames whose motion would exceed it split
|
|
36
|
+
* into internal sub-steps; smaller is tighter and costs more solver
|
|
37
|
+
* work.
|
|
38
|
+
* @default 0.1
|
|
39
|
+
*/
|
|
40
|
+
couplingTolerance?: number;
|
|
41
|
+
/**
|
|
42
|
+
* When springs snap to their targets instead of animating: `'user'`
|
|
43
|
+
* follows the OS prefers-reduced-motion setting, including live
|
|
44
|
+
* changes; `'always'` and `'never'` force one behavior.
|
|
45
|
+
* @default 'user'
|
|
46
|
+
*/
|
|
8
47
|
reducedMotion?: 'user' | 'always' | 'never';
|
|
9
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* The Nuxt module: creates an app-wide spring system (started on the
|
|
51
|
+
* client), auto-imports the composables and helpers `useSpring`,
|
|
52
|
+
* `useSpringSystem`, `useSpringPool`, `defineSpring`, `mapSpring`,
|
|
53
|
+
* `velocityOf`, and `accelerationOf`, and registers the `SpringValue`
|
|
54
|
+
* component. Types are left out — they would be noise in most apps.
|
|
55
|
+
*/
|
|
10
56
|
declare const coilyModule: NuxtModule;
|
|
11
57
|
//#endregion
|
|
12
58
|
export { CoilyModuleOptions, coilyModule as default };
|
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'
|
|
@@ -21,6 +22,13 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
21
22
|
})
|
|
22
23
|
})`;
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The Nuxt module: creates an app-wide spring system (started on the
|
|
27
|
+
* client), auto-imports the composables and helpers `useSpring`,
|
|
28
|
+
* `useSpringSystem`, `useSpringPool`, `defineSpring`, `mapSpring`,
|
|
29
|
+
* `velocityOf`, and `accelerationOf`, and registers the `SpringValue`
|
|
30
|
+
* component. Types are left out — they would be noise in most apps.
|
|
31
|
+
*/
|
|
24
32
|
const coilyModule = defineNuxtModule({
|
|
25
33
|
meta: {
|
|
26
34
|
name: "coily",
|
|
@@ -48,28 +56,20 @@ const coilyModule = defineNuxtModule({
|
|
|
48
56
|
name: "useSpringPool"
|
|
49
57
|
},
|
|
50
58
|
{
|
|
51
|
-
from: "coily
|
|
52
|
-
name: "
|
|
53
|
-
type: true
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
from: "coily/vue",
|
|
57
|
-
name: "SpringObjectRef",
|
|
58
|
-
type: true
|
|
59
|
+
from: "coily",
|
|
60
|
+
name: "defineSpring"
|
|
59
61
|
},
|
|
60
62
|
{
|
|
61
|
-
from: "coily
|
|
62
|
-
name: "
|
|
63
|
-
type: true
|
|
63
|
+
from: "coily",
|
|
64
|
+
name: "mapSpring"
|
|
64
65
|
},
|
|
65
66
|
{
|
|
66
67
|
from: "coily",
|
|
67
|
-
name: "
|
|
68
|
+
name: "velocityOf"
|
|
68
69
|
},
|
|
69
70
|
{
|
|
70
71
|
from: "coily",
|
|
71
|
-
name: "
|
|
72
|
-
type: true
|
|
72
|
+
name: "accelerationOf"
|
|
73
73
|
}
|
|
74
74
|
]);
|
|
75
75
|
addComponent({
|