effect-motion 0.4.0 → 0.6.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 +3 -3
- package/dist/Camera.d.ts +186 -49
- package/dist/Camera.js +343 -76
- package/dist/Color.d.ts +101 -1
- package/dist/Color.js +101 -1
- package/dist/EffectMotionError.d.ts +17 -0
- package/dist/EffectMotionError.js +17 -0
- package/dist/Entity.d.ts +684 -30
- package/dist/Entity.js +281 -27
- package/dist/Font.d.ts +108 -0
- package/dist/Font.js +95 -0
- package/dist/Image.d.ts +71 -0
- package/dist/Image.js +50 -0
- package/dist/Instance.d.ts +73 -11
- package/dist/Instance.js +44 -11
- package/dist/Motion.d.ts +328 -53
- package/dist/Motion.js +278 -46
- package/dist/Physics.d.ts +154 -20
- package/dist/Physics.js +92 -11
- package/dist/Projection.d.ts +36 -132
- package/dist/Projection.js +33 -292
- package/dist/Resource.d.ts +26 -0
- package/dist/Resource.js +41 -0
- package/dist/Runner.d.ts +714 -349
- package/dist/Runner.js +197 -150
- package/dist/Scene.d.ts +603 -175
- package/dist/Scene.js +596 -112
- package/dist/Timing.d.ts +170 -13
- package/dist/Timing.js +124 -6
- package/dist/Tree.d.ts +39 -0
- package/dist/Tree.js +127 -0
- package/dist/index.d.ts +54 -5
- package/dist/index.js +56 -5
- package/dist/particles/Particle.d.ts +2 -2
- package/dist/particles/ParticleField.d.ts +44 -42
- package/dist/particles/ParticleField.js +8 -9
- package/dist/particles/constructors.d.ts +5 -6
- package/dist/particles/constructors.js +8 -3
- package/dist/particles/legacy.d.ts +58 -0
- package/dist/particles/legacy.js +46 -0
- package/dist/particles/simulate.js +11 -3
- package/dist/particles/step.js +14 -10
- package/dist/types.d.ts +5 -0
- package/dist/types.js +1 -0
- package/package.json +58 -60
- package/dist/CameraHelpers.d.ts +0 -70
- package/dist/CameraHelpers.js +0 -239
- package/dist/CanvasExporter.d.ts +0 -12
- package/dist/CanvasExporter.js +0 -40
- package/dist/Fonts.d.ts +0 -41
- package/dist/Fonts.js +0 -27
- package/dist/Images.d.ts +0 -33
- package/dist/Images.js +0 -24
- package/dist/PngExporter.d.ts +0 -6
- package/dist/PngExporter.js +0 -85
- package/dist/Renderer.d.ts +0 -118
- package/dist/Renderer.js +0 -508
- package/dist/Shapes.d.ts +0 -11
- package/dist/Shapes.js +0 -11
- package/dist/demo.d.ts +0 -3
- package/dist/demo.js +0 -71
- package/dist/render/dof.d.ts +0 -27
- package/dist/render/dof.js +0 -37
- package/dist/render/paint.d.ts +0 -30
- package/dist/render/paint.js +0 -36
- package/dist/render/shapes.d.ts +0 -42
- package/dist/render/shapes.js +0 -310
- package/dist/shapes/Circle.d.ts +0 -32
- package/dist/shapes/Circle.js +0 -9
- package/dist/shapes/Ellipse.d.ts +0 -35
- package/dist/shapes/Ellipse.js +0 -10
- package/dist/shapes/Group.d.ts +0 -126
- package/dist/shapes/Group.js +0 -89
- package/dist/shapes/Hud.d.ts +0 -38
- package/dist/shapes/Hud.js +0 -35
- package/dist/shapes/Image.d.ts +0 -45
- package/dist/shapes/Image.js +0 -28
- package/dist/shapes/Line.d.ts +0 -47
- package/dist/shapes/Line.js +0 -35
- package/dist/shapes/Path.d.ts +0 -107
- package/dist/shapes/Path.js +0 -32
- package/dist/shapes/Rect.d.ts +0 -51
- package/dist/shapes/Rect.js +0 -22
- package/dist/shapes/Shape2D.d.ts +0 -49
- package/dist/shapes/Shape2D.js +0 -52
- package/dist/shapes/Shapes.d.ts +0 -11
- package/dist/shapes/Shapes.js +0 -11
- package/dist/shapes/Square.d.ts +0 -32
- package/dist/shapes/Square.js +0 -11
- package/dist/shapes/Text.d.ts +0 -51
- package/dist/shapes/Text.js +0 -24
package/dist/Timing.d.ts
CHANGED
|
@@ -1,20 +1,90 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* Easing curves — the shape of an animation's pacing over its duration.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* An easing maps linear progress `t` (0 → 1) to eased progress. It changes
|
|
6
|
+
* only WHEN a value gets where it's going, never where it ends up, so
|
|
7
|
+
* swapping curves restyles motion without touching its endpoints.
|
|
8
|
+
*
|
|
9
|
+
* Pass any of these to an animator by name — `"easeOutCubic"` — which
|
|
10
|
+
* autocompletes and is the usual form. A `TimingFunction` of your own is
|
|
11
|
+
* accepted anywhere a name is.
|
|
12
|
+
*
|
|
13
|
+
* Choosing one, in short:
|
|
14
|
+
*
|
|
15
|
+
* - `linear` — mechanical, constant speed. Good for continuous motion
|
|
16
|
+
* (rotation, scrolling), stiff for anything that starts or stops.
|
|
17
|
+
* - `easeOut*` — fast then settling. The default instinct for something
|
|
18
|
+
* ARRIVING; the motion is legible immediately.
|
|
19
|
+
* - `easeIn*` — slow then accelerating. For something LEAVING.
|
|
20
|
+
* - `easeInOut*` — eased at both ends; the natural choice for a move
|
|
21
|
+
* between two resting states.
|
|
22
|
+
* - Within each family, `Sine` is the gentlest and `Quad` → `Cubic` →
|
|
23
|
+
* `Quart` → `Quint` → `Expo` progressively more pronounced. `Circ` is
|
|
24
|
+
* sharper still near the ends.
|
|
25
|
+
* - `Back`, `Elastic`, `Bounce` — overshoot and oscillation, for character.
|
|
26
|
+
* These are how you get spring-LIKE motion in a fixed duration; for true
|
|
27
|
+
* physics with an emergent duration, use `Physics` instead.
|
|
28
|
+
*
|
|
29
|
+
* Every non-periodic easing satisfies f(0) = 0 and f(1) = 1, which is what
|
|
30
|
+
* guarantees a tween lands exactly on its target. `Back` and `Elastic`
|
|
31
|
+
* deliberately leave [0, 1] mid-animation — that overshoot IS the effect, so
|
|
32
|
+
* consumers extrapolate rather than clamp. `Bounce` stays within [0, 1]: it
|
|
33
|
+
* rebounds AWAY from the target rather than past it. `sin` and `cos` are the
|
|
34
|
+
* exceptions to the endpoint rule: they trace a full periodic cycle and
|
|
35
|
+
* return to where they started.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* By name, and with a custom curve.
|
|
39
|
+
* ```typescript
|
|
40
|
+
* yield* box.pipe(Motion.moveTo({ x: 400 }, "1 second", "easeInOutCubic"));
|
|
41
|
+
* yield* box.pipe(Motion.moveTo({ x: 0 }, "1 second", (t) => t * t));
|
|
42
|
+
* ```
|
|
8
43
|
*/
|
|
9
44
|
export type TimingFunction = (t: number) => number;
|
|
45
|
+
/**
|
|
46
|
+
* No easing: constant speed from start to finish.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* The default when no timing is given. Right for continuous motion that
|
|
50
|
+
* neither starts nor stops on screen; abrupt for anything that does.
|
|
51
|
+
*
|
|
52
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
53
|
+
*/
|
|
10
54
|
export declare const linear: TimingFunction;
|
|
11
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* One full sine cycle: 0 → 1 → 0.
|
|
57
|
+
*
|
|
58
|
+
* @remarks
|
|
59
|
+
* Periodic, so it deliberately RETURNS to its starting value instead of
|
|
60
|
+
* ending at 1 — a tween using it finishes where it began. That makes it a
|
|
61
|
+
* there-and-back helper (a pulse, a sway), not a transition between two
|
|
62
|
+
* states.
|
|
63
|
+
*/
|
|
12
64
|
export declare const sin: TimingFunction;
|
|
13
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* One full cosine cycle: 1 → 0 → 1.
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* {@link sin} phase-shifted: starts at the far end, dips, and comes back.
|
|
70
|
+
* Also periodic, so it does not end at 1.
|
|
71
|
+
*/
|
|
14
72
|
export declare const cos: TimingFunction;
|
|
73
|
+
/**
|
|
74
|
+
* The Sine family — the gentlest easing. Barely-there acceleration, for
|
|
75
|
+
* motion that should feel eased without drawing attention.
|
|
76
|
+
*
|
|
77
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
78
|
+
*/
|
|
15
79
|
export declare const easeInSine: TimingFunction;
|
|
16
80
|
export declare const easeOutSine: TimingFunction;
|
|
17
81
|
export declare const easeInOutSine: TimingFunction;
|
|
82
|
+
/**
|
|
83
|
+
* The polynomial families, from gentlest to most pronounced: Quad (t²),
|
|
84
|
+
* Cubic (t³), Quart (t⁴), Quint (t⁵). Cubic is the everyday workhorse.
|
|
85
|
+
*
|
|
86
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
87
|
+
*/
|
|
18
88
|
export declare const easeInQuad: TimingFunction;
|
|
19
89
|
export declare const easeOutQuad: TimingFunction;
|
|
20
90
|
export declare const easeInOutQuad: TimingFunction;
|
|
@@ -27,25 +97,100 @@ export declare const easeInOutQuart: TimingFunction;
|
|
|
27
97
|
export declare const easeInQuint: TimingFunction;
|
|
28
98
|
export declare const easeOutQuint: TimingFunction;
|
|
29
99
|
export declare const easeInOutQuint: TimingFunction;
|
|
100
|
+
/**
|
|
101
|
+
* The Expo family — the most extreme of the smooth curves: near-motionless
|
|
102
|
+
* at the slow end, very fast at the other. For dramatic arrivals and exits.
|
|
103
|
+
*
|
|
104
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
105
|
+
*/
|
|
30
106
|
export declare const easeInExpo: TimingFunction;
|
|
31
107
|
export declare const easeOutExpo: TimingFunction;
|
|
32
108
|
export declare const easeInOutExpo: TimingFunction;
|
|
109
|
+
/**
|
|
110
|
+
* The Circ family — a quarter-circle arc. Sharper at the ends than the
|
|
111
|
+
* polynomials, with a distinctly mechanical, geometric feel.
|
|
112
|
+
*
|
|
113
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
114
|
+
*/
|
|
33
115
|
export declare const easeInCirc: TimingFunction;
|
|
34
116
|
export declare const easeOutCirc: TimingFunction;
|
|
35
117
|
export declare const easeInOutCirc: TimingFunction;
|
|
36
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Build a Back easing with a custom overshoot amount.
|
|
120
|
+
*
|
|
121
|
+
* @remarks
|
|
122
|
+
* Back curves pull slightly PAST the target and come back — the
|
|
123
|
+
* anticipation that makes an entrance feel deliberate. Use the ready-made
|
|
124
|
+
* {@link easeInBack} / {@link easeOutBack} / {@link easeInOutBack} unless
|
|
125
|
+
* you specifically want a different amount of overshoot.
|
|
126
|
+
*
|
|
127
|
+
* @param s - Overshoot amount; larger goes further past the target.
|
|
128
|
+
* @defaultValue `1.70158` — the canonical ~10% overshoot
|
|
129
|
+
* @returns A {@link TimingFunction} to pass to any animator.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* const subtle = Timing.createEaseOutBack(0.7);
|
|
134
|
+
* yield* card.pipe(Motion.moveTo({ y: 100 }, "600 millis", subtle));
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
37
137
|
export declare const createEaseInBack: (s?: number) => TimingFunction;
|
|
38
138
|
export declare const createEaseOutBack: (s?: number) => TimingFunction;
|
|
39
139
|
export declare const createEaseInOutBack: (s?: number, v?: number) => TimingFunction;
|
|
40
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* Build an Elastic easing with a custom oscillation frequency.
|
|
142
|
+
*
|
|
143
|
+
* @remarks
|
|
144
|
+
* Elastic curves overshoot and ring like a plucked string before settling.
|
|
145
|
+
* Prefer {@link easeInElastic} / {@link easeOutElastic} /
|
|
146
|
+
* {@link easeInOutElastic} unless you want a different wobble rate.
|
|
147
|
+
*
|
|
148
|
+
* @param s - Angular frequency; higher oscillates more times.
|
|
149
|
+
* @defaultValue `2.094395` (2π/3)
|
|
150
|
+
* @returns A {@link TimingFunction} to pass to any animator.
|
|
151
|
+
*/
|
|
41
152
|
export declare const createEaseInElastic: (s?: number) => TimingFunction;
|
|
42
153
|
export declare const createEaseOutElastic: (s?: number) => TimingFunction;
|
|
43
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* Build an in-out Elastic easing with a custom oscillation frequency.
|
|
156
|
+
*
|
|
157
|
+
* @param s - Angular frequency; higher oscillates more times.
|
|
158
|
+
* @defaultValue `1.39626` (2π/4.5)
|
|
159
|
+
* @returns A {@link TimingFunction} to pass to any animator.
|
|
160
|
+
*/
|
|
44
161
|
export declare const createEaseInOutElastic: (s?: number) => TimingFunction;
|
|
45
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Build a Bounce easing with custom stiffness and bounce spacing.
|
|
164
|
+
*
|
|
165
|
+
* @remarks
|
|
166
|
+
* Bounce curves imitate a ball hitting a surface: a series of ever-smaller
|
|
167
|
+
* rebounds. Unlike Elastic, they never go PAST the target — they arrive and
|
|
168
|
+
* rebound away from it. Prefer {@link easeOutBounce} and its siblings unless
|
|
169
|
+
* tuning the character.
|
|
170
|
+
*
|
|
171
|
+
* The offsets are derived from the parameters, so f(1) = 1 holds for any
|
|
172
|
+
* stiffness and a tween still lands exactly.
|
|
173
|
+
*
|
|
174
|
+
* @param n - Bounce stiffness.
|
|
175
|
+
* @param d - Interval divisor, setting how the bounces are spaced.
|
|
176
|
+
* @defaultValue `n` — `7.5625`; `d` — `2.75` (the canonical CSS bounce)
|
|
177
|
+
* @returns A {@link TimingFunction} to pass to any animator.
|
|
178
|
+
*/
|
|
46
179
|
export declare const createEaseOutBounce: (n?: number, d?: number) => TimingFunction;
|
|
47
180
|
export declare const createEaseInBounce: (n?: number, d?: number) => TimingFunction;
|
|
48
181
|
export declare const createEaseInOutBounce: (n?: number, d?: number) => TimingFunction;
|
|
182
|
+
/**
|
|
183
|
+
* The ready-made overshoot and oscillation curves, at their canonical
|
|
184
|
+
* parameters — `Back` anticipates past the target, `Elastic` rings like a
|
|
185
|
+
* string, `Bounce` rebounds like a dropped ball.
|
|
186
|
+
*
|
|
187
|
+
* @remarks
|
|
188
|
+
* These are how you get spring-LIKE character in a fixed, known duration.
|
|
189
|
+
* For real physics whose length emerges from the simulation, use `Physics`.
|
|
190
|
+
*
|
|
191
|
+
* @see {@link createEaseInBack}, {@link createEaseInElastic},
|
|
192
|
+
* {@link createEaseOutBounce} to tune the parameters.
|
|
193
|
+
*/
|
|
49
194
|
export declare const easeInBack: TimingFunction;
|
|
50
195
|
export declare const easeOutBack: TimingFunction;
|
|
51
196
|
export declare const easeInOutBack: TimingFunction;
|
|
@@ -55,6 +200,14 @@ export declare const easeInOutElastic: TimingFunction;
|
|
|
55
200
|
export declare const easeInBounce: TimingFunction;
|
|
56
201
|
export declare const easeOutBounce: TimingFunction;
|
|
57
202
|
export declare const easeInOutBounce: TimingFunction;
|
|
203
|
+
/**
|
|
204
|
+
* Every built-in easing, by name — the lookup behind the string form.
|
|
205
|
+
*
|
|
206
|
+
* @remarks
|
|
207
|
+
* You rarely touch this directly: passing `"easeOutCubic"` to an animator
|
|
208
|
+
* resolves through it. Useful for building a curve picker, or iterating the
|
|
209
|
+
* whole set.
|
|
210
|
+
*/
|
|
58
211
|
export declare const timingFunctions: {
|
|
59
212
|
readonly linear: TimingFunction;
|
|
60
213
|
readonly sin: TimingFunction;
|
|
@@ -90,7 +243,11 @@ export declare const timingFunctions: {
|
|
|
90
243
|
readonly easeOutBounce: TimingFunction;
|
|
91
244
|
readonly easeInOutBounce: TimingFunction;
|
|
92
245
|
};
|
|
246
|
+
/** The name of a built-in easing — what animators autocomplete. */
|
|
93
247
|
export type TimingFunctionName = keyof typeof timingFunctions;
|
|
94
|
-
/**
|
|
248
|
+
/**
|
|
249
|
+
* What animators accept for pacing: a built-in easing name, or your own
|
|
250
|
+
* `(t: number) => number`.
|
|
251
|
+
*/
|
|
95
252
|
export type TimingInput = TimingFunctionName | TimingFunction;
|
|
96
253
|
export declare const resolve: (input: TimingInput) => TimingFunction;
|
package/dist/Timing.js
CHANGED
|
@@ -1,14 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No easing: constant speed from start to finish.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* The default when no timing is given. Right for continuous motion that
|
|
6
|
+
* neither starts nor stops on screen; abrupt for anything that does.
|
|
7
|
+
*
|
|
8
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
9
|
+
*/
|
|
1
10
|
export const linear = (t) => t;
|
|
2
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* One full sine cycle: 0 → 1 → 0.
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* Periodic, so it deliberately RETURNS to its starting value instead of
|
|
16
|
+
* ending at 1 — a tween using it finishes where it began. That makes it a
|
|
17
|
+
* there-and-back helper (a pulse, a sway), not a transition between two
|
|
18
|
+
* states.
|
|
19
|
+
*/
|
|
3
20
|
export const sin = (t) => (1 - Math.cos(2 * Math.PI * t)) / 2;
|
|
4
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* One full cosine cycle: 1 → 0 → 1.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* {@link sin} phase-shifted: starts at the far end, dips, and comes back.
|
|
26
|
+
* Also periodic, so it does not end at 1.
|
|
27
|
+
*/
|
|
5
28
|
export const cos = (t) => (1 + Math.cos(2 * Math.PI * t)) / 2;
|
|
29
|
+
/**
|
|
30
|
+
* The Sine family — the gentlest easing. Barely-there acceleration, for
|
|
31
|
+
* motion that should feel eased without drawing attention.
|
|
32
|
+
*
|
|
33
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
34
|
+
*/
|
|
6
35
|
export const easeInSine = (t) => 1 - Math.cos((t * Math.PI) / 2);
|
|
7
36
|
export const easeOutSine = (t) => Math.sin((t * Math.PI) / 2);
|
|
8
37
|
export const easeInOutSine = (t) => -(Math.cos(Math.PI * t) - 1) / 2;
|
|
9
38
|
const easeInPow = (p) => (t) => t ** p;
|
|
10
39
|
const easeOutPow = (p) => (t) => 1 - (1 - t) ** p;
|
|
11
40
|
const easeInOutPow = (p) => (t) => t < 0.5 ? 2 ** (p - 1) * t ** p : 1 - (-2 * t + 2) ** p / 2;
|
|
41
|
+
/**
|
|
42
|
+
* The polynomial families, from gentlest to most pronounced: Quad (t²),
|
|
43
|
+
* Cubic (t³), Quart (t⁴), Quint (t⁵). Cubic is the everyday workhorse.
|
|
44
|
+
*
|
|
45
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
46
|
+
*/
|
|
12
47
|
export const easeInQuad = easeInPow(2);
|
|
13
48
|
export const easeOutQuad = easeOutPow(2);
|
|
14
49
|
export const easeInOutQuad = easeInOutPow(2);
|
|
@@ -21,6 +56,12 @@ export const easeInOutQuart = easeInOutPow(4);
|
|
|
21
56
|
export const easeInQuint = easeInPow(5);
|
|
22
57
|
export const easeOutQuint = easeOutPow(5);
|
|
23
58
|
export const easeInOutQuint = easeInOutPow(5);
|
|
59
|
+
/**
|
|
60
|
+
* The Expo family — the most extreme of the smooth curves: near-motionless
|
|
61
|
+
* at the slow end, very fast at the other. For dramatic arrivals and exits.
|
|
62
|
+
*
|
|
63
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
64
|
+
*/
|
|
24
65
|
export const easeInExpo = (t) => t === 0 ? 0 : 2 ** (10 * t - 10);
|
|
25
66
|
export const easeOutExpo = (t) => t === 1 ? 1 : 1 - 2 ** (-10 * t);
|
|
26
67
|
export const easeInOutExpo = (t) => t === 0
|
|
@@ -30,12 +71,36 @@ export const easeInOutExpo = (t) => t === 0
|
|
|
30
71
|
: t < 0.5
|
|
31
72
|
? 2 ** (20 * t - 10) / 2
|
|
32
73
|
: (2 - 2 ** (-20 * t + 10)) / 2;
|
|
74
|
+
/**
|
|
75
|
+
* The Circ family — a quarter-circle arc. Sharper at the ends than the
|
|
76
|
+
* polynomials, with a distinctly mechanical, geometric feel.
|
|
77
|
+
*
|
|
78
|
+
* @see {@link TimingFunction} for choosing among the families.
|
|
79
|
+
*/
|
|
33
80
|
export const easeInCirc = (t) => 1 - Math.sqrt(1 - t ** 2);
|
|
34
81
|
export const easeOutCirc = (t) => Math.sqrt(1 - (t - 1) ** 2);
|
|
35
82
|
export const easeInOutCirc = (t) => t < 0.5
|
|
36
83
|
? (1 - Math.sqrt(1 - (2 * t) ** 2)) / 2
|
|
37
84
|
: (Math.sqrt(1 - (-2 * t + 2) ** 2) + 1) / 2;
|
|
38
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Build a Back easing with a custom overshoot amount.
|
|
87
|
+
*
|
|
88
|
+
* @remarks
|
|
89
|
+
* Back curves pull slightly PAST the target and come back — the
|
|
90
|
+
* anticipation that makes an entrance feel deliberate. Use the ready-made
|
|
91
|
+
* {@link easeInBack} / {@link easeOutBack} / {@link easeInOutBack} unless
|
|
92
|
+
* you specifically want a different amount of overshoot.
|
|
93
|
+
*
|
|
94
|
+
* @param s - Overshoot amount; larger goes further past the target.
|
|
95
|
+
* @defaultValue `1.70158` — the canonical ~10% overshoot
|
|
96
|
+
* @returns A {@link TimingFunction} to pass to any animator.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const subtle = Timing.createEaseOutBack(0.7);
|
|
101
|
+
* yield* card.pipe(Motion.moveTo({ y: 100 }, "600 millis", subtle));
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
39
104
|
export const createEaseInBack = (s = 1.70158) => (t) => (s + 1) * t ** 3 - s * t ** 2;
|
|
40
105
|
export const createEaseOutBack = (s = 1.70158) => (t) => 1 + (s + 1) * (t - 1) ** 3 + s * (t - 1) ** 2;
|
|
41
106
|
export const createEaseInOutBack = (s = 1.70158, v = 1.525) => {
|
|
@@ -44,7 +109,18 @@ export const createEaseInOutBack = (s = 1.70158, v = 1.525) => {
|
|
|
44
109
|
? ((2 * t) ** 2 * ((c + 1) * 2 * t - c)) / 2
|
|
45
110
|
: ((2 * t - 2) ** 2 * ((c + 1) * (2 * t - 2) + c) + 2) / 2;
|
|
46
111
|
};
|
|
47
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Build an Elastic easing with a custom oscillation frequency.
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* Elastic curves overshoot and ring like a plucked string before settling.
|
|
117
|
+
* Prefer {@link easeInElastic} / {@link easeOutElastic} /
|
|
118
|
+
* {@link easeInOutElastic} unless you want a different wobble rate.
|
|
119
|
+
*
|
|
120
|
+
* @param s - Angular frequency; higher oscillates more times.
|
|
121
|
+
* @defaultValue `2.094395` (2π/3)
|
|
122
|
+
* @returns A {@link TimingFunction} to pass to any animator.
|
|
123
|
+
*/
|
|
48
124
|
export const createEaseInElastic = (s = 2.094395) => (t) => t === 0
|
|
49
125
|
? 0
|
|
50
126
|
: t === 1
|
|
@@ -55,7 +131,13 @@ export const createEaseOutElastic = (s = 2.094395) => (t) => t === 0
|
|
|
55
131
|
: t === 1
|
|
56
132
|
? 1
|
|
57
133
|
: 2 ** (-10 * t) * Math.sin((t * 10 - 0.75) * s) + 1;
|
|
58
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Build an in-out Elastic easing with a custom oscillation frequency.
|
|
136
|
+
*
|
|
137
|
+
* @param s - Angular frequency; higher oscillates more times.
|
|
138
|
+
* @defaultValue `1.39626` (2π/4.5)
|
|
139
|
+
* @returns A {@link TimingFunction} to pass to any animator.
|
|
140
|
+
*/
|
|
59
141
|
export const createEaseInOutElastic = (s = 1.39626) => (t) => t === 0
|
|
60
142
|
? 0
|
|
61
143
|
: t === 1
|
|
@@ -63,7 +145,23 @@ export const createEaseInOutElastic = (s = 1.39626) => (t) => t === 0
|
|
|
63
145
|
: t < 0.5
|
|
64
146
|
? -(2 ** (20 * t - 10) * Math.sin((20 * t - 11.125) * s)) / 2
|
|
65
147
|
: (2 ** (-20 * t + 10) * Math.sin((20 * t - 11.125) * s)) / 2 + 1;
|
|
66
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Build a Bounce easing with custom stiffness and bounce spacing.
|
|
150
|
+
*
|
|
151
|
+
* @remarks
|
|
152
|
+
* Bounce curves imitate a ball hitting a surface: a series of ever-smaller
|
|
153
|
+
* rebounds. Unlike Elastic, they never go PAST the target — they arrive and
|
|
154
|
+
* rebound away from it. Prefer {@link easeOutBounce} and its siblings unless
|
|
155
|
+
* tuning the character.
|
|
156
|
+
*
|
|
157
|
+
* The offsets are derived from the parameters, so f(1) = 1 holds for any
|
|
158
|
+
* stiffness and a tween still lands exactly.
|
|
159
|
+
*
|
|
160
|
+
* @param n - Bounce stiffness.
|
|
161
|
+
* @param d - Interval divisor, setting how the bounces are spaced.
|
|
162
|
+
* @defaultValue `n` — `7.5625`; `d` — `2.75` (the canonical CSS bounce)
|
|
163
|
+
* @returns A {@link TimingFunction} to pass to any animator.
|
|
164
|
+
*/
|
|
67
165
|
export const createEaseOutBounce = (n = 7.5625, d = 2.75) => {
|
|
68
166
|
// segment offsets derived from the parameters so f(1) = 1 holds for
|
|
69
167
|
// any stiffness (defaults yield the canonical 0.75/0.9375/0.984375)
|
|
@@ -94,6 +192,18 @@ export const createEaseInOutBounce = (n = 7.5625, d = 2.75) => {
|
|
|
94
192
|
const out = createEaseOutBounce(n, d);
|
|
95
193
|
return (t) => (t < 0.5 ? (1 - out(1 - 2 * t)) / 2 : (1 + out(2 * t - 1)) / 2);
|
|
96
194
|
};
|
|
195
|
+
/**
|
|
196
|
+
* The ready-made overshoot and oscillation curves, at their canonical
|
|
197
|
+
* parameters — `Back` anticipates past the target, `Elastic` rings like a
|
|
198
|
+
* string, `Bounce` rebounds like a dropped ball.
|
|
199
|
+
*
|
|
200
|
+
* @remarks
|
|
201
|
+
* These are how you get spring-LIKE character in a fixed, known duration.
|
|
202
|
+
* For real physics whose length emerges from the simulation, use `Physics`.
|
|
203
|
+
*
|
|
204
|
+
* @see {@link createEaseInBack}, {@link createEaseInElastic},
|
|
205
|
+
* {@link createEaseOutBounce} to tune the parameters.
|
|
206
|
+
*/
|
|
97
207
|
export const easeInBack = createEaseInBack();
|
|
98
208
|
export const easeOutBack = createEaseOutBack();
|
|
99
209
|
export const easeInOutBack = createEaseInOutBack();
|
|
@@ -103,6 +213,14 @@ export const easeInOutElastic = createEaseInOutElastic();
|
|
|
103
213
|
export const easeInBounce = createEaseInBounce();
|
|
104
214
|
export const easeOutBounce = createEaseOutBounce();
|
|
105
215
|
export const easeInOutBounce = createEaseInOutBounce();
|
|
216
|
+
/**
|
|
217
|
+
* Every built-in easing, by name — the lookup behind the string form.
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* You rarely touch this directly: passing `"easeOutCubic"` to an animator
|
|
221
|
+
* resolves through it. Useful for building a curve picker, or iterating the
|
|
222
|
+
* whole set.
|
|
223
|
+
*/
|
|
106
224
|
export const timingFunctions = {
|
|
107
225
|
linear,
|
|
108
226
|
sin,
|
package/dist/Tree.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as Entity from "./Entity.js";
|
|
2
|
+
/**
|
|
3
|
+
* The runner's scene graph: id → entry, plus parent/child bookkeeping.
|
|
4
|
+
*
|
|
5
|
+
* An entry holds the entity's current state as a member of the closed union
|
|
6
|
+
* (`Entity.ts`), so reading a field means narrowing on `_tag` rather than
|
|
7
|
+
* casting from `{}`. The entity DEFINITION is not stored — it is resolved
|
|
8
|
+
* from the state's tag when construction is needed.
|
|
9
|
+
*/
|
|
10
|
+
/** conventional id of the implicit root group every instance attaches to */
|
|
11
|
+
export declare const ROOT_ID = "root";
|
|
12
|
+
export interface Entry<Tag extends Entity.EntityTag = Entity.EntityTag> {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
state: Entity.EntityByTag<Tag>;
|
|
15
|
+
parentId: string | null;
|
|
16
|
+
}
|
|
17
|
+
export type Entryish = Entry | string;
|
|
18
|
+
export declare class Tree {
|
|
19
|
+
private idCounter;
|
|
20
|
+
readonly map: Record<string, Entry>;
|
|
21
|
+
/**
|
|
22
|
+
* Returns a plain `Entry`, not `Entry<Tag>`: `state` is mutable, so a
|
|
23
|
+
* tagged entry is invariant and cannot live in a map of mixed tags. The
|
|
24
|
+
* tag lives on the caller's `Instance<Tag>` instead, which is immutable
|
|
25
|
+
* and where the narrowing is actually wanted.
|
|
26
|
+
*/
|
|
27
|
+
createNode: (state: Entity.Entity, id?: string) => Entry;
|
|
28
|
+
getEntry: (entryish: Entryish) => Entry | null;
|
|
29
|
+
/**
|
|
30
|
+
* Frames snapshot `entry.state` by reference, so a child-list update must
|
|
31
|
+
* produce a FRESH object — mutating in place would rewrite frames that
|
|
32
|
+
* were already emitted.
|
|
33
|
+
*/
|
|
34
|
+
private setChildren;
|
|
35
|
+
removeFromParent: (entryish: Entryish) => void;
|
|
36
|
+
appendChild: (parentish: Entryish, childish: Entryish) => void;
|
|
37
|
+
insertBefore: (childish: Entryish, beforeish: Entryish) => void;
|
|
38
|
+
remove: (entryish: Entryish) => void;
|
|
39
|
+
}
|
package/dist/Tree.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as Entity from "./Entity.js";
|
|
2
|
+
/**
|
|
3
|
+
* The runner's scene graph: id → entry, plus parent/child bookkeeping.
|
|
4
|
+
*
|
|
5
|
+
* An entry holds the entity's current state as a member of the closed union
|
|
6
|
+
* (`Entity.ts`), so reading a field means narrowing on `_tag` rather than
|
|
7
|
+
* casting from `{}`. The entity DEFINITION is not stored — it is resolved
|
|
8
|
+
* from the state's tag when construction is needed.
|
|
9
|
+
*/
|
|
10
|
+
/** conventional id of the implicit root group every instance attaches to */
|
|
11
|
+
export const ROOT_ID = "root";
|
|
12
|
+
const idOf = (entryish) => typeof entryish === "string" ? entryish : entryish.id;
|
|
13
|
+
const nodeNotFound = (entryish) => {
|
|
14
|
+
throw new Error(`Runner: node "${idOf(entryish)}" not found`);
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A parent that cannot hold children. Under the closed union this is knowable
|
|
18
|
+
* from the tag, so the message names what the entity actually is.
|
|
19
|
+
*/
|
|
20
|
+
const notAContainer = (entry) => {
|
|
21
|
+
throw new Error(`Runner: parent "${entry.id}" is a ${entry.state._tag}, which cannot have children`);
|
|
22
|
+
};
|
|
23
|
+
/** the entry's children, or a loud failure if its entity is not a container */
|
|
24
|
+
const childrenOrDie = (entry) => Entity.isContainer(entry.state) ? entry.state.children : notAContainer(entry);
|
|
25
|
+
export class Tree {
|
|
26
|
+
idCounter = 0;
|
|
27
|
+
map = {
|
|
28
|
+
[ROOT_ID]: {
|
|
29
|
+
id: ROOT_ID,
|
|
30
|
+
state: Entity.Group.make({}),
|
|
31
|
+
parentId: null,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Returns a plain `Entry`, not `Entry<Tag>`: `state` is mutable, so a
|
|
36
|
+
* tagged entry is invariant and cannot live in a map of mixed tags. The
|
|
37
|
+
* tag lives on the caller's `Instance<Tag>` instead, which is immutable
|
|
38
|
+
* and where the narrowing is actually wanted.
|
|
39
|
+
*/
|
|
40
|
+
createNode = (state,
|
|
41
|
+
// engine-owned singletons (the built-in camera) claim a fixed id
|
|
42
|
+
id = `${state._tag}_${this.idCounter++}`) => {
|
|
43
|
+
const entry = { id, state, parentId: null };
|
|
44
|
+
this.map[id] = entry;
|
|
45
|
+
return entry;
|
|
46
|
+
};
|
|
47
|
+
getEntry = (entryish) => this.map[idOf(entryish)] ?? null;
|
|
48
|
+
/**
|
|
49
|
+
* Frames snapshot `entry.state` by reference, so a child-list update must
|
|
50
|
+
* produce a FRESH object — mutating in place would rewrite frames that
|
|
51
|
+
* were already emitted.
|
|
52
|
+
*/
|
|
53
|
+
setChildren = (parent, children) => {
|
|
54
|
+
const state = parent.state;
|
|
55
|
+
// switch, not a cast: spreading a UNION member widens back to the union
|
|
56
|
+
// and loses the tag, so narrow to the concrete tag first and let each
|
|
57
|
+
// branch produce a well-typed Group/Hud.
|
|
58
|
+
switch (state._tag) {
|
|
59
|
+
case "Group":
|
|
60
|
+
parent.state = { ...state, children };
|
|
61
|
+
return;
|
|
62
|
+
case "Hud":
|
|
63
|
+
parent.state = { ...state, children };
|
|
64
|
+
return;
|
|
65
|
+
default:
|
|
66
|
+
notAContainer(parent);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
removeFromParent = (entryish) => {
|
|
70
|
+
const entry = this.getEntry(entryish) ?? nodeNotFound(entryish);
|
|
71
|
+
if (entry.parentId === null) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// a parent that was itself removed: nothing to filter, just detach
|
|
75
|
+
const parent = this.getEntry(entry.parentId);
|
|
76
|
+
if (parent !== null) {
|
|
77
|
+
this.setChildren(parent, childrenOrDie(parent).filter((childId) => childId !== entry.id));
|
|
78
|
+
}
|
|
79
|
+
entry.parentId = null;
|
|
80
|
+
};
|
|
81
|
+
appendChild = (parentish, childish) => {
|
|
82
|
+
const child = this.getEntry(childish) ?? nodeNotFound(childish);
|
|
83
|
+
this.removeFromParent(child);
|
|
84
|
+
const parent = this.getEntry(parentish) ?? nodeNotFound(parentish);
|
|
85
|
+
this.setChildren(parent, [...childrenOrDie(parent), child.id]);
|
|
86
|
+
child.parentId = parent.id;
|
|
87
|
+
};
|
|
88
|
+
insertBefore = (childish, beforeish) => {
|
|
89
|
+
const child = this.getEntry(childish) ?? nodeNotFound(childish);
|
|
90
|
+
const before = this.getEntry(beforeish) ?? nodeNotFound(beforeish);
|
|
91
|
+
this.removeFromParent(child);
|
|
92
|
+
if (before.parentId === null) {
|
|
93
|
+
throw new Error(`Runner: before "${before.id}" is not a child`);
|
|
94
|
+
}
|
|
95
|
+
const parent = this.getEntry(before.parentId) ?? nodeNotFound(before.parentId);
|
|
96
|
+
const children = [];
|
|
97
|
+
let inserted = false;
|
|
98
|
+
for (const childId of childrenOrDie(parent)) {
|
|
99
|
+
if (childId === before.id) {
|
|
100
|
+
children.push(child.id);
|
|
101
|
+
inserted = true;
|
|
102
|
+
}
|
|
103
|
+
children.push(childId);
|
|
104
|
+
}
|
|
105
|
+
if (!inserted) {
|
|
106
|
+
children.push(child.id);
|
|
107
|
+
}
|
|
108
|
+
this.setChildren(parent, children);
|
|
109
|
+
child.parentId = parent.id;
|
|
110
|
+
};
|
|
111
|
+
remove = (entryish) => {
|
|
112
|
+
const entry = this.getEntry(entryish) ?? nodeNotFound(entryish);
|
|
113
|
+
this.removeFromParent(entry);
|
|
114
|
+
delete this.map[entry.id];
|
|
115
|
+
// orphan its children, and backstop-scan child lists: stays correct even
|
|
116
|
+
// after manual reparenting via raw data updates (which bypass parentId)
|
|
117
|
+
for (const other of Object.values(this.map)) {
|
|
118
|
+
if (other.parentId === entry.id) {
|
|
119
|
+
other.parentId = null;
|
|
120
|
+
}
|
|
121
|
+
if (Entity.isContainer(other.state) &&
|
|
122
|
+
other.state.children.includes(entry.id)) {
|
|
123
|
+
this.setChildren(other, other.state.children.filter((childId) => childId !== entry.id));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* effect-motion — deterministic, frame-exact motion graphics in code.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Scenes are pure descriptions of animation: the same scene produces the
|
|
6
|
+
* same frames on every run and every machine, because time is counted in
|
|
7
|
+
* frames rather than read from a clock and randomness comes from a seed.
|
|
8
|
+
*
|
|
9
|
+
* Where to start:
|
|
10
|
+
*
|
|
11
|
+
* - `Scene` — declare a scene, create entities, compose animations, run it.
|
|
12
|
+
* - `Motion` — animate over a duration, with easing.
|
|
13
|
+
* - `Physics` — animate with springs, whose length emerges from the sim.
|
|
14
|
+
* - `Entity` — the shapes, and `vec3` for positions.
|
|
15
|
+
* - `Color`, `Timing` — the palette and the easing curves.
|
|
16
|
+
* - `Camera` — aiming, orbiting, and dollying the viewpoint.
|
|
17
|
+
*
|
|
18
|
+
* Prefer deep per-actor imports over this barrel:
|
|
19
|
+
*
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import * as Motion from "effect-motion/Motion";
|
|
22
|
+
* import * as Scene from "effect-motion/Scene";
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* A complete scene.
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import * as Color from "effect-motion/Color";
|
|
29
|
+
* import * as Motion from "effect-motion/Motion";
|
|
30
|
+
* import * as Scene from "effect-motion/Scene";
|
|
31
|
+
*
|
|
32
|
+
* export const scene = Scene.make(
|
|
33
|
+
* function* () {
|
|
34
|
+
* const dot = yield* Scene.instantiate("Circle", {
|
|
35
|
+
* radius: 24,
|
|
36
|
+
* fillColor: Color.hex("#7f5af0"),
|
|
37
|
+
* });
|
|
38
|
+
* yield* dot.pipe(
|
|
39
|
+
* Motion.moveTo({ x: 430 }, "1 second", "easeInOutCubic"),
|
|
40
|
+
* Motion.fadeTo(0, "400 millis"),
|
|
41
|
+
* );
|
|
42
|
+
* },
|
|
43
|
+
* { width: 500, height: 300, backgroundColor: Color.rgba(22, 22, 29) },
|
|
44
|
+
* );
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @packageDocumentation
|
|
48
|
+
*/
|
|
49
|
+
export * as Camera from "./Camera.js";
|
|
2
50
|
export * as Color from "./Color.js";
|
|
51
|
+
export { EffectMotionError } from "./EffectMotionError.js";
|
|
3
52
|
export * as Entity from "./Entity.js";
|
|
4
|
-
export * as
|
|
5
|
-
export * as
|
|
53
|
+
export * as Font from "./Font.js";
|
|
54
|
+
export * as Image from "./Image.js";
|
|
6
55
|
export * as Instance from "./Instance.js";
|
|
7
56
|
export * as Motion from "./Motion.js";
|
|
8
57
|
export * as Phaser from "./Phaser.js";
|
|
9
58
|
export * as Physics from "./Physics.js";
|
|
10
59
|
export * as Particles from "./particles/index.js";
|
|
11
|
-
export * as
|
|
60
|
+
export * as Resource from "./Resource.js";
|
|
61
|
+
export * as Runner from "./Runner.js";
|
|
12
62
|
export * as Scene from "./Scene.js";
|
|
13
|
-
export * as Shapes from "./Shapes.js";
|
|
14
63
|
export * as Timing from "./Timing.js";
|