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.
Files changed (91) hide show
  1. package/README.md +3 -3
  2. package/dist/Camera.d.ts +186 -49
  3. package/dist/Camera.js +343 -76
  4. package/dist/Color.d.ts +101 -1
  5. package/dist/Color.js +101 -1
  6. package/dist/EffectMotionError.d.ts +17 -0
  7. package/dist/EffectMotionError.js +17 -0
  8. package/dist/Entity.d.ts +684 -30
  9. package/dist/Entity.js +281 -27
  10. package/dist/Font.d.ts +108 -0
  11. package/dist/Font.js +95 -0
  12. package/dist/Image.d.ts +71 -0
  13. package/dist/Image.js +50 -0
  14. package/dist/Instance.d.ts +73 -11
  15. package/dist/Instance.js +44 -11
  16. package/dist/Motion.d.ts +328 -53
  17. package/dist/Motion.js +278 -46
  18. package/dist/Physics.d.ts +154 -20
  19. package/dist/Physics.js +92 -11
  20. package/dist/Projection.d.ts +36 -132
  21. package/dist/Projection.js +33 -292
  22. package/dist/Resource.d.ts +26 -0
  23. package/dist/Resource.js +41 -0
  24. package/dist/Runner.d.ts +714 -349
  25. package/dist/Runner.js +197 -150
  26. package/dist/Scene.d.ts +603 -175
  27. package/dist/Scene.js +596 -112
  28. package/dist/Timing.d.ts +170 -13
  29. package/dist/Timing.js +124 -6
  30. package/dist/Tree.d.ts +39 -0
  31. package/dist/Tree.js +127 -0
  32. package/dist/index.d.ts +54 -5
  33. package/dist/index.js +56 -5
  34. package/dist/particles/Particle.d.ts +2 -2
  35. package/dist/particles/ParticleField.d.ts +44 -42
  36. package/dist/particles/ParticleField.js +8 -9
  37. package/dist/particles/constructors.d.ts +5 -6
  38. package/dist/particles/constructors.js +8 -3
  39. package/dist/particles/legacy.d.ts +58 -0
  40. package/dist/particles/legacy.js +46 -0
  41. package/dist/particles/simulate.js +11 -3
  42. package/dist/particles/step.js +14 -10
  43. package/dist/types.d.ts +5 -0
  44. package/dist/types.js +1 -0
  45. package/package.json +58 -60
  46. package/dist/CameraHelpers.d.ts +0 -70
  47. package/dist/CameraHelpers.js +0 -239
  48. package/dist/CanvasExporter.d.ts +0 -12
  49. package/dist/CanvasExporter.js +0 -40
  50. package/dist/Fonts.d.ts +0 -41
  51. package/dist/Fonts.js +0 -27
  52. package/dist/Images.d.ts +0 -33
  53. package/dist/Images.js +0 -24
  54. package/dist/PngExporter.d.ts +0 -6
  55. package/dist/PngExporter.js +0 -85
  56. package/dist/Renderer.d.ts +0 -118
  57. package/dist/Renderer.js +0 -508
  58. package/dist/Shapes.d.ts +0 -11
  59. package/dist/Shapes.js +0 -11
  60. package/dist/demo.d.ts +0 -3
  61. package/dist/demo.js +0 -71
  62. package/dist/render/dof.d.ts +0 -27
  63. package/dist/render/dof.js +0 -37
  64. package/dist/render/paint.d.ts +0 -30
  65. package/dist/render/paint.js +0 -36
  66. package/dist/render/shapes.d.ts +0 -42
  67. package/dist/render/shapes.js +0 -310
  68. package/dist/shapes/Circle.d.ts +0 -32
  69. package/dist/shapes/Circle.js +0 -9
  70. package/dist/shapes/Ellipse.d.ts +0 -35
  71. package/dist/shapes/Ellipse.js +0 -10
  72. package/dist/shapes/Group.d.ts +0 -126
  73. package/dist/shapes/Group.js +0 -89
  74. package/dist/shapes/Hud.d.ts +0 -38
  75. package/dist/shapes/Hud.js +0 -35
  76. package/dist/shapes/Image.d.ts +0 -45
  77. package/dist/shapes/Image.js +0 -28
  78. package/dist/shapes/Line.d.ts +0 -47
  79. package/dist/shapes/Line.js +0 -35
  80. package/dist/shapes/Path.d.ts +0 -107
  81. package/dist/shapes/Path.js +0 -32
  82. package/dist/shapes/Rect.d.ts +0 -51
  83. package/dist/shapes/Rect.js +0 -22
  84. package/dist/shapes/Shape2D.d.ts +0 -49
  85. package/dist/shapes/Shape2D.js +0 -52
  86. package/dist/shapes/Shapes.d.ts +0 -11
  87. package/dist/shapes/Shapes.js +0 -11
  88. package/dist/shapes/Square.d.ts +0 -32
  89. package/dist/shapes/Square.js +0 -11
  90. package/dist/shapes/Text.d.ts +0 -51
  91. package/dist/shapes/Text.js +0 -24
package/dist/Timing.d.ts CHANGED
@@ -1,20 +1,90 @@
1
1
  /**
2
- * Timing (easing) functions: map linear progress t in [0, 1] to eased
3
- * progress. Every non-periodic easing satisfies f(0) = 0 and f(1) = 1,
4
- * so tweens land exactly on their target. `sin`/`cos` are periodic
5
- * helpers over one full cycle and deliberately do NOT end at 1.
6
- * Back/Elastic curves overshoot outside [0, 1] mid-animation by design —
7
- * consumers must extrapolate, not clamp.
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
- /** one full sine cycle: 0 → 1 → 0 */
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
- /** one full cosine cycle: 1 → 0 → 1 */
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
- /** `s` is the overshoot amount */
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
- /** `s` is the angular frequency (default 2π/3) */
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
- /** `s` is the angular frequency (default 2π/4.5) */
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
- /** `n` is the bounce stiffness, `d` the interval divisor */
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
- /** a built-in name (autocompleted) or a custom timing function */
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
- /** one full sine cycle: 0 → 1 → 0 */
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
- /** one full cosine cycle: 1 → 0 → 1 */
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
- /** `s` is the overshoot amount */
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
- /** `s` is the angular frequency (default 2π/3) */
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
- /** `s` is the angular frequency (default 2π/4.5) */
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
- /** `n` is the bounce stiffness, `d` the interval divisor */
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
- export * as Camera from "./CameraHelpers.js";
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 Fonts from "./Fonts.js";
5
- export * as Images from "./Images.js";
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 Renderer from "./Renderer.js";
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";