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/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# effect-motion
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Frame-exact motion graphics in code, composed with [Effect](https://effect.website).
|
|
4
4
|
|
|
5
|
-
A scene is an Effect generator program: instantiate entities, then tween or spring their properties, composing motions sequentially or in parallel. Scenes are **
|
|
5
|
+
A scene is an Effect generator program: instantiate entities, then tween or spring their properties, composing motions sequentially or in parallel. Scenes are **frame-exact** — animation is counted in whole frames at the scene's frame rate, never read from a wall clock — and **finite**, which is what lets a scene be scrubbed and replayed like a video.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
`effect` is a peer dependency — install it alongside:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
|
|
12
|
+
bun add effect-motion effect
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Write a scene
|
package/dist/Camera.d.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import * as Duration from "effect/Duration";
|
|
3
|
+
import * as Instance from "./Instance.js";
|
|
4
|
+
import * as Motion from "./Motion.js";
|
|
5
|
+
import type * as Projection from "./Projection.js";
|
|
6
|
+
import * as Runner from "./Runner.js";
|
|
7
|
+
import * as Timing from "./Timing.js";
|
|
4
8
|
/**
|
|
5
9
|
* The camera is view state, not a shape — it is never registered with a sink
|
|
6
10
|
* and never drawn. It exists as an ordinary Instance so the existing
|
|
7
11
|
* animators drive it for free: `camera.pipe(moveTo({ z: -800 }))`,
|
|
8
12
|
* `tween("rotY", ...)`, `spring`, `Scene.fork`, etc.
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
14
|
+
* `position` is the camera's world position, `rotation` its Euler
|
|
15
|
+
* orientation, and `focalLength` sets the field of view. At rest
|
|
12
16
|
* the camera sits a focal-length back on +z looking down world -z, so a
|
|
13
17
|
* world point at z=0 projects to plain-2D screen coordinates — see
|
|
14
18
|
* `Projection.ts`. The sink reads these off `FrameMeta.camera` and projects
|
|
@@ -22,50 +26,183 @@ import * as Projection from "./Projection.js";
|
|
|
22
26
|
* instance, so by the time animators or the renderer read the data they are
|
|
23
27
|
* always concrete.
|
|
24
28
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
y: Schema.withConstructorDefault<Schema.Number>;
|
|
28
|
-
z: Schema.withConstructorDefault<Schema.Number>;
|
|
29
|
-
rotX: Schema.withConstructorDefault<Schema.Number>;
|
|
30
|
-
rotY: Schema.withConstructorDefault<Schema.Number>;
|
|
31
|
-
rotZ: Schema.withConstructorDefault<Schema.Number>;
|
|
32
|
-
focalLength: Schema.withConstructorDefault<Schema.Number>;
|
|
33
|
-
focusDistance: Schema.withConstructorDefault<Schema.Number>;
|
|
34
|
-
aperture: Schema.withConstructorDefault<Schema.Number>;
|
|
35
|
-
poiX: Schema.optionalKey<Schema.Number>;
|
|
36
|
-
poiY: Schema.optionalKey<Schema.Number>;
|
|
37
|
-
poiZ: Schema.optionalKey<Schema.Number>;
|
|
38
|
-
};
|
|
39
|
-
type CameraData = Schema.Struct<typeof fields>["Type"];
|
|
40
|
-
export declare const Camera: Entity.Entity<"Camera", Schema.Struct<{
|
|
41
|
-
x: Schema.withConstructorDefault<Schema.Number>;
|
|
42
|
-
y: Schema.withConstructorDefault<Schema.Number>;
|
|
43
|
-
z: Schema.withConstructorDefault<Schema.Number>;
|
|
44
|
-
rotX: Schema.withConstructorDefault<Schema.Number>;
|
|
45
|
-
rotY: Schema.withConstructorDefault<Schema.Number>;
|
|
46
|
-
rotZ: Schema.withConstructorDefault<Schema.Number>;
|
|
47
|
-
focalLength: Schema.withConstructorDefault<Schema.Number>;
|
|
48
|
-
focusDistance: Schema.withConstructorDefault<Schema.Number>;
|
|
49
|
-
aperture: Schema.withConstructorDefault<Schema.Number>;
|
|
50
|
-
poiX: Schema.optionalKey<Schema.Number>;
|
|
51
|
-
poiY: Schema.optionalKey<Schema.Number>;
|
|
52
|
-
poiZ: Schema.optionalKey<Schema.Number>;
|
|
53
|
-
}>, {
|
|
54
|
-
readonly "~position": {
|
|
55
|
-
get: (data: CameraData) => {
|
|
56
|
-
x: number;
|
|
57
|
-
y: number;
|
|
58
|
-
z: number;
|
|
59
|
-
};
|
|
60
|
-
set: (data: CameraData, value: Entity.Position) => CameraData;
|
|
61
|
-
};
|
|
62
|
-
}>;
|
|
29
|
+
/** the camera view as a frame carries it */
|
|
30
|
+
export type CameraState = Projection.CameraView & Projection.PointOfInterest;
|
|
63
31
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
32
|
+
* What the camera helpers accept as something to aim at.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* An instance is read LIVE each frame, so aiming tracks it as it moves — the
|
|
36
|
+
* usual case. A plain position is fixed, for aiming at a spot where no
|
|
37
|
+
* entity exists.
|
|
67
38
|
*/
|
|
68
|
-
export
|
|
69
|
-
|
|
70
|
-
|
|
39
|
+
export type CameraTarget = Instance.Instance | Effect.Effect<Instance.Instance, never, Runner.Runner> | Partial<Motion.Position>;
|
|
40
|
+
type CamOrEffect<E = never, R = Runner.Runner> = Instance.InstanceOrEffect<"Camera", E, R>;
|
|
41
|
+
type CamInstance = Instance.Instance<"Camera">;
|
|
42
|
+
type CamEffect = Effect.Effect<CamInstance, never, Runner.Runner>;
|
|
43
|
+
/**
|
|
44
|
+
* Point the camera at something.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* Aiming is expressed as a point of interest rather than as rotation
|
|
48
|
+
* angles, which is what makes it composable: the target can be a live
|
|
49
|
+
* instance, and the camera keeps facing it as it moves. There is no
|
|
50
|
+
* `lookAtTo` — the verb already names its target — so an optional
|
|
51
|
+
* `duration` selects between the two behaviors:
|
|
52
|
+
*
|
|
53
|
+
* - **Omitted** — aim snaps this frame. Use it to establish the shot before
|
|
54
|
+
* anything moves.
|
|
55
|
+
* - **Given** — the aim eases over that time as a RETARGETED tween: each
|
|
56
|
+
* frame interpolates toward the target's current position, so it converges
|
|
57
|
+
* exactly onto a moving target with no snap at the end.
|
|
58
|
+
*
|
|
59
|
+
* Setting a point of interest is also the prerequisite for
|
|
60
|
+
* {@link orbitTo} and {@link dollyTo}, which are both defined relative to it.
|
|
61
|
+
*
|
|
62
|
+
* @param target - An instance to track, or a fixed world position.
|
|
63
|
+
* @param duration - Omit to snap; give a time to ease the re-aim.
|
|
64
|
+
* @param timing - An easing name or function.
|
|
65
|
+
* @param offset - Shifts the aim relative to the target, e.g. slightly above.
|
|
66
|
+
* @defaultValue `timing` — `"linear"`
|
|
67
|
+
* @returns The camera, so animators chain.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* Snap to establish, then ease across to a second subject.
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const camera = yield* Scene.camera;
|
|
73
|
+
* yield* camera.pipe(Camera.lookAt(hero));
|
|
74
|
+
* yield* camera.pipe(Camera.lookAt(villain, "1 second", "easeInOutCubic"));
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare const lookAt: ((target: CameraTarget, duration?: Duration.Input, timing?: Timing.TimingInput, offset?: Partial<Motion.Position>) => (cam: CamOrEffect) => CamEffect) & ((cam: CamOrEffect, target: CameraTarget, duration?: Duration.Input, timing?: Timing.TimingInput, offset?: Partial<Motion.Position>) => CamEffect);
|
|
78
|
+
/**
|
|
79
|
+
* Keep the camera locked onto a moving target for `duration`.
|
|
80
|
+
*
|
|
81
|
+
* @remarks
|
|
82
|
+
* Where {@link lookAt} with a duration EASES toward a target and stops,
|
|
83
|
+
* `follow` holds the aim on it: every frame copies the target's position, so
|
|
84
|
+
* the subject stays pinned while it moves. Run it concurrently with the
|
|
85
|
+
* subject's own animation — typically inside `Scene.all` or a `Scene.fork`.
|
|
86
|
+
*
|
|
87
|
+
* There is no timing parameter, because tracking is a hard per-frame copy
|
|
88
|
+
* rather than an interpolation. For a lagging, weighted camera, animate the
|
|
89
|
+
* point of interest with a spring instead.
|
|
90
|
+
*
|
|
91
|
+
* Ordering note: within a frame, branches run in the order they were forked.
|
|
92
|
+
* A follow forked BEFORE its target's animator reads the previous frame's
|
|
93
|
+
* position — a deterministic one-frame trail, not a bug.
|
|
94
|
+
*
|
|
95
|
+
* @param target - An instance to track, or a fixed world position.
|
|
96
|
+
* @param duration - How long to keep tracking.
|
|
97
|
+
* @param offset - Shifts the aim relative to the target.
|
|
98
|
+
* @returns The camera, so animators chain.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* Track the runner for the two seconds it is crossing frame.
|
|
102
|
+
* ```typescript
|
|
103
|
+
* yield* Scene.all([
|
|
104
|
+
* runner.pipe(Motion.moveTo({ x: 900 }, "2 seconds")),
|
|
105
|
+
* camera.pipe(Camera.follow(runner, "2 seconds")),
|
|
106
|
+
* ]);
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export declare const follow: ((target: CameraTarget, duration: Duration.Input, offset?: Partial<Motion.Position>) => (cam: CamOrEffect) => CamEffect) & ((cam: CamOrEffect, target: CameraTarget, duration: Duration.Input, offset?: Partial<Motion.Position>) => CamEffect);
|
|
110
|
+
/**
|
|
111
|
+
* Swing the camera around its point of interest, like a turntable.
|
|
112
|
+
*
|
|
113
|
+
* @remarks
|
|
114
|
+
* The camera travels an arc at a fixed radius and height while continuing to
|
|
115
|
+
* face the subject — the standard "orbit the product" move. Only the
|
|
116
|
+
* position is animated; the aim follows from the point of interest, so there
|
|
117
|
+
* is no orientation math to get wrong.
|
|
118
|
+
*
|
|
119
|
+
* `azimuth` is an ABSOLUTE angle in radians about the world-Y axis through
|
|
120
|
+
* the point of interest, where 0 is directly in front (+z of it). A full
|
|
121
|
+
* turn is `Math.PI * 2`; the sign chooses direction.
|
|
122
|
+
*
|
|
123
|
+
* Requires a point of interest — call {@link lookAt} first, or this fails
|
|
124
|
+
* loudly telling you so.
|
|
125
|
+
*
|
|
126
|
+
* @param azimuth - Target angle in radians; 0 is directly in front.
|
|
127
|
+
* @param duration - How long, in scene time.
|
|
128
|
+
* @param timing - An easing name or function.
|
|
129
|
+
* @defaultValue `timing` — `"linear"`
|
|
130
|
+
* @returns The camera, so animators chain.
|
|
131
|
+
* @see {@link orbit} to start from an explicit angle.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* A quarter turn around a subject.
|
|
135
|
+
* ```typescript
|
|
136
|
+
* yield* camera.pipe(
|
|
137
|
+
* Camera.lookAt(subject),
|
|
138
|
+
* Camera.orbitTo(Math.PI / 2, "2 seconds", "easeInOutCubic"),
|
|
139
|
+
* );
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
export declare const orbitTo: ((azimuth: number, duration: Duration.Input, timing?: Timing.TimingInput) => (cam: CamOrEffect) => CamEffect) & ((cam: CamOrEffect, azimuth: number, duration: Duration.Input, timing?: Timing.TimingInput) => CamEffect);
|
|
143
|
+
/**
|
|
144
|
+
* Like {@link orbitTo}, but starting from an explicit azimuth.
|
|
145
|
+
*
|
|
146
|
+
* @remarks
|
|
147
|
+
* Stating both ends is what makes a full revolution expressible: `orbit(0,
|
|
148
|
+
* Math.PI * 2, …)` sweeps all the way around, whereas `orbitTo(Math.PI * 2)`
|
|
149
|
+
* from a resting camera would already be at its target and not move.
|
|
150
|
+
*
|
|
151
|
+
* @param from - Starting angle in radians.
|
|
152
|
+
* @param to - Target angle in radians.
|
|
153
|
+
* @param duration - How long, in scene time.
|
|
154
|
+
* @param timing - An easing name or function.
|
|
155
|
+
* @returns The camera, so animators chain.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* One full revolution.
|
|
159
|
+
* ```typescript
|
|
160
|
+
* yield* camera.pipe(Camera.orbit(0, Math.PI * 2, "4 seconds"));
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export declare const orbit: ((from: number, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => (cam: CamOrEffect) => CamEffect) & ((cam: CamOrEffect, from: number, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => CamEffect);
|
|
164
|
+
/**
|
|
165
|
+
* Move the camera toward or away from its point of interest along the view
|
|
166
|
+
* axis.
|
|
167
|
+
*
|
|
168
|
+
* @remarks
|
|
169
|
+
* A push-in or pull-back that keeps the subject centered and the aim
|
|
170
|
+
* unchanged — only the distance changes. Distinct from tweening the camera's
|
|
171
|
+
* `z`, which moves along the WORLD axis and would slide the subject off
|
|
172
|
+
* center once the camera is aimed obliquely.
|
|
173
|
+
*
|
|
174
|
+
* Requires a point of interest — call {@link lookAt} first.
|
|
175
|
+
*
|
|
176
|
+
* @param distance - Target distance from the point of interest; smaller is
|
|
177
|
+
* closer.
|
|
178
|
+
* @param duration - How long, in scene time.
|
|
179
|
+
* @param timing - An easing name or function.
|
|
180
|
+
* @defaultValue `timing` — `"linear"`
|
|
181
|
+
* @returns The camera, so animators chain.
|
|
182
|
+
* @see {@link dolly} to start from an explicit distance.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* Push in on a subject.
|
|
186
|
+
* ```typescript
|
|
187
|
+
* yield* camera.pipe(
|
|
188
|
+
* Camera.lookAt(subject),
|
|
189
|
+
* Camera.dollyTo(300, "1500 millis", "easeInOutCubic"),
|
|
190
|
+
* );
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export declare const dollyTo: ((distance: number, duration: Duration.Input, timing?: Timing.TimingInput) => (cam: CamOrEffect) => CamEffect) & ((cam: CamOrEffect, distance: number, duration: Duration.Input, timing?: Timing.TimingInput) => CamEffect);
|
|
194
|
+
/**
|
|
195
|
+
* Like {@link dollyTo}, but starting from an explicit distance.
|
|
196
|
+
*
|
|
197
|
+
* @remarks
|
|
198
|
+
* For a push-in that begins further out than the camera currently sits —
|
|
199
|
+
* the move starts at `from` regardless of where the camera was left.
|
|
200
|
+
*
|
|
201
|
+
* @param from - Starting distance from the point of interest.
|
|
202
|
+
* @param to - Target distance.
|
|
203
|
+
* @param duration - How long, in scene time.
|
|
204
|
+
* @param timing - An easing name or function.
|
|
205
|
+
* @returns The camera, so animators chain.
|
|
206
|
+
*/
|
|
207
|
+
export declare const dolly: ((from: number, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => (cam: CamOrEffect) => CamEffect) & ((cam: CamOrEffect, from: number, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => CamEffect);
|
|
71
208
|
export {};
|