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/Instance.d.ts
CHANGED
|
@@ -1,16 +1,78 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Pipeable from "effect/Pipeable";
|
|
3
|
-
import type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import type { EntityByTag, EntityTag } from "./Entity.js";
|
|
4
|
+
/**
|
|
5
|
+
* An Instance is a REFERENCE to a live entity in the runner tree: an id and
|
|
6
|
+
* the tag of what it is. It carries no entity data — the runner owns that —
|
|
7
|
+
* so it stays a tiny, pipeable handle that animator chains flow through.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* A handle to something living in a scene — what `Scene.instantiate` returns
|
|
11
|
+
* and what every animator takes.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* An instance holds no entity data of its own, only an id and a kind. The
|
|
15
|
+
* data lives in the scene and changes every frame; the handle stays valid
|
|
16
|
+
* throughout, which is why it can be captured once and animated repeatedly.
|
|
17
|
+
* To read the current values, use `Scene.data`.
|
|
18
|
+
*
|
|
19
|
+
* The `Tag` parameter is what makes animators statically safe: `fadeTo`
|
|
20
|
+
* accepts only instances whose entity actually has an `opacity`, so fading a
|
|
21
|
+
* Camera fails at compile time with a message naming the missing field
|
|
22
|
+
* rather than misbehaving at runtime.
|
|
23
|
+
*
|
|
24
|
+
* Instances are Pipeable — that is what lets `circle.pipe(moveTo(…),
|
|
25
|
+
* fadeTo(…))` read as one chain. A handle is NOT itself an Effect, so
|
|
26
|
+
* anything accepting one in Effect position lifts it with
|
|
27
|
+
* {@link flattenInstance} first.
|
|
28
|
+
*
|
|
29
|
+
* @typeParam Tag - Which entity this refers to, e.g. `"Circle"`.
|
|
30
|
+
*/
|
|
31
|
+
export interface Instance<Tag extends EntityTag = EntityTag> extends Pipeable.Pipeable {
|
|
32
|
+
readonly _tag: "Instance";
|
|
8
33
|
readonly id: string;
|
|
9
|
-
readonly
|
|
34
|
+
readonly kind: Tag;
|
|
10
35
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Build an instance handle from an id and a kind.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* Internal plumbing — the scene runner calls this when creating entities.
|
|
41
|
+
* Author code gets handles from `Scene.instantiate` instead; constructing
|
|
42
|
+
* one by hand refers to an entity that may not exist.
|
|
43
|
+
*/
|
|
44
|
+
export declare const makeInstance: <Tag extends EntityTag>(id: string, kind: Tag) => Instance<Tag>;
|
|
45
|
+
/**
|
|
46
|
+
* Whether `u` is an {@link Instance}.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* The dispatch behind every dual animator: `moveTo(circle, …)` and
|
|
50
|
+
* `circle.pipe(moveTo(…))` are told apart by testing the first argument
|
|
51
|
+
* with this, rather than by counting arguments — trailing optional
|
|
52
|
+
* parameters make arity ambiguous.
|
|
53
|
+
*/
|
|
13
54
|
export declare const isInstance: (u: unknown) => u is Instance;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Whether `u` is an instance of one specific entity kind.
|
|
57
|
+
*
|
|
58
|
+
* @remarks
|
|
59
|
+
* Narrows to `Instance<Tag>`, so a heterogeneous list can be filtered down
|
|
60
|
+
* to the circles and then animated with circle-only fields.
|
|
61
|
+
*
|
|
62
|
+
* @param tag - The kind to test for, e.g. `"Circle"`.
|
|
63
|
+
* @param u - The value to test.
|
|
64
|
+
*/
|
|
65
|
+
export declare const isInstanceOf: <Tag extends EntityTag>(tag: Tag, u: unknown) => u is Instance<Tag>;
|
|
66
|
+
/**
|
|
67
|
+
* An instance, or an un-yielded `instantiate` effect that produces one.
|
|
68
|
+
*
|
|
69
|
+
* @remarks
|
|
70
|
+
* Animators accept both, so a scene can animate straight off an
|
|
71
|
+
* `instantiate` call without binding it to a variable first — the create
|
|
72
|
+
* step and the first animation read as one expression.
|
|
73
|
+
*/
|
|
74
|
+
export type InstanceOrEffect<Tag extends EntityTag = EntityTag, E = never, R = never> = Instance<Tag> | Effect.Effect<Instance<Tag>, E, R>;
|
|
75
|
+
/** Resolve an {@link InstanceOrEffect} to the instance itself. */
|
|
76
|
+
export declare const flattenInstance: <Tag extends EntityTag, E = never, R = never>(instance: InstanceOrEffect<Tag, E, R>) => Effect.Effect<Instance<Tag>, E, R>;
|
|
77
|
+
/** The entity data type a given instance handle refers to. */
|
|
78
|
+
export type DataOf<I> = I extends Instance<infer Tag> ? EntityByTag<Tag> : never;
|
package/dist/Instance.js
CHANGED
|
@@ -1,18 +1,51 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Pipeable from "effect/Pipeable";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Instances are Pipeable so animator chains read data-last:
|
|
5
|
+
* `circle.pipe(moveTo(...), fadeTo(...))`. That is the authored form in most
|
|
6
|
+
* scenes, so the protocol is load-bearing, not a convenience.
|
|
7
|
+
*/
|
|
8
|
+
const InstanceProto = {
|
|
9
|
+
_tag: "Instance",
|
|
7
10
|
pipe() {
|
|
8
11
|
// biome-ignore lint: lint/style/noArguments: Pipeable's variadic protocol
|
|
9
12
|
return Pipeable.pipeArguments(this, arguments);
|
|
10
13
|
},
|
|
11
14
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Build an instance handle from an id and a kind.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* Internal plumbing — the scene runner calls this when creating entities.
|
|
20
|
+
* Author code gets handles from `Scene.instantiate` instead; constructing
|
|
21
|
+
* one by hand refers to an entity that may not exist.
|
|
22
|
+
*/
|
|
23
|
+
export const makeInstance = (id, kind) => Object.assign(Object.create(InstanceProto), { id, kind });
|
|
24
|
+
/**
|
|
25
|
+
* Whether `u` is an {@link Instance}.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* The dispatch behind every dual animator: `moveTo(circle, …)` and
|
|
29
|
+
* `circle.pipe(moveTo(…))` are told apart by testing the first argument
|
|
30
|
+
* with this, rather than by counting arguments — trailing optional
|
|
31
|
+
* parameters make arity ambiguous.
|
|
32
|
+
*/
|
|
33
|
+
export const isInstance = (u) => typeof u === "object" &&
|
|
34
|
+
u !== null &&
|
|
35
|
+
"_tag" in u &&
|
|
36
|
+
u._tag === "Instance";
|
|
37
|
+
/**
|
|
38
|
+
* Whether `u` is an instance of one specific entity kind.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* Narrows to `Instance<Tag>`, so a heterogeneous list can be filtered down
|
|
42
|
+
* to the circles and then animated with circle-only fields.
|
|
43
|
+
*
|
|
44
|
+
* @param tag - The kind to test for, e.g. `"Circle"`.
|
|
45
|
+
* @param u - The value to test.
|
|
46
|
+
*/
|
|
47
|
+
export const isInstanceOf = (tag, u) => isInstance(u) && u.kind === tag;
|
|
48
|
+
/** Resolve an {@link InstanceOrEffect} to the instance itself. */
|
|
49
|
+
export const flattenInstance = (instance) => isInstance(instance)
|
|
50
|
+
? Effect.succeed(instance)
|
|
51
|
+
: instance;
|
package/dist/Motion.d.ts
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duration-based animation: the animators that interpolate a value over a
|
|
3
|
+
* span of scene time, paced by an easing curve.
|
|
4
|
+
*
|
|
5
|
+
* The module is two layers over one engine:
|
|
6
|
+
*
|
|
7
|
+
* - **raw** — {@link tween} / {@link tweenTo} interpolate numeric fields BY
|
|
8
|
+
* NAME (`radius`, `width`, `fontSize`), whatever the entity's schema
|
|
9
|
+
* declares.
|
|
10
|
+
* - **semantic** — {@link move} / {@link moveTo} and {@link fade} /
|
|
11
|
+
* {@link fadeTo} speak in concepts rather than field names, and are what
|
|
12
|
+
* you should reach for when one exists: geometry is relative to
|
|
13
|
+
* `position`, so moving a Line translates both endpoints and moving a
|
|
14
|
+
* Group carries its whole subtree, with no per-entity handling.
|
|
15
|
+
*
|
|
16
|
+
* Every animator ships as a base/To pair — `verbTo` starts from the
|
|
17
|
+
* instance's CURRENT value, `verb` takes an explicit origin — and each is a
|
|
18
|
+
* dual, callable data-first (`moveTo(circle, …)`) or pipeable
|
|
19
|
+
* (`circle.pipe(moveTo(…))`). They resolve with the instance, so chains
|
|
20
|
+
* compose. For durationless motion whose length emerges from a simulation,
|
|
21
|
+
* see `Physics`.
|
|
22
|
+
*
|
|
23
|
+
* Timing is frame-exact: a `duration` is converted to whole frames at the
|
|
24
|
+
* runner's frame rate, and the last frame receives exactly the target value
|
|
25
|
+
* for any easing with f(1) = 1 — a tween never lands "almost" on its
|
|
26
|
+
* target. A zero-length duration still consumes one frame.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* Chain a raw tween into two semantic animators. `1 second` at 30fps is 30
|
|
30
|
+
* animation frames, and `radius`/`position`/`opacity` all land exactly.
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import * as Motion from "effect-motion/Motion";
|
|
33
|
+
* import * as Scene from "effect-motion/Scene";
|
|
34
|
+
*
|
|
35
|
+
* const scene = Scene.make(function* () {
|
|
36
|
+
* const dot = yield* Scene.instantiate("Circle", { radius: 4 });
|
|
37
|
+
* yield* dot.pipe(
|
|
38
|
+
* Motion.tweenTo({ radius: 40 }, "1 second", "easeOutCubic"),
|
|
39
|
+
* Motion.moveTo({ x: 300 }, "1 second"),
|
|
40
|
+
* Motion.fadeTo(0, "500 millis"),
|
|
41
|
+
* );
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
1
45
|
import type * as Duration from "effect/Duration";
|
|
2
46
|
import * as Effect from "effect/Effect";
|
|
3
|
-
import type * as Schema from "effect/Schema";
|
|
4
47
|
import * as Color from "./Color.js";
|
|
5
48
|
import * as Entity from "./Entity.js";
|
|
6
49
|
import * as Instance from "./Instance.js";
|
|
@@ -16,76 +59,308 @@ export type InterpolableOnly<T> = Pick<T, InterpolableKeys<T>>;
|
|
|
16
59
|
type InterpolableOrInterpolator<T> = {
|
|
17
60
|
[K in keyof T]-?: NonNullable<T[K]> extends number ? number : NonNullable<T[K]> extends Color.Color ? Color.Color | ColorInterpolator : never;
|
|
18
61
|
};
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
62
|
+
/**
|
|
63
|
+
* What {@link tween} / {@link tweenTo} accept as an endpoint: the fields to
|
|
64
|
+
* animate and their values.
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* Two forms. A plain object states targets literally. A function receives
|
|
68
|
+
* the entity's data at animation start and computes them, which is how you
|
|
69
|
+
* express a target relative to where the entity already is (`data.radius *
|
|
70
|
+
* 2`) without reading it out in a separate step.
|
|
71
|
+
*
|
|
72
|
+
* Only numeric and Color fields of the entity are addressable — those are
|
|
73
|
+
* the ones that can be interpolated.
|
|
74
|
+
*
|
|
75
|
+
* @typeParam Tag - The entity tag, which determines the available fields.
|
|
76
|
+
*/
|
|
77
|
+
export type Target<Tag extends Entity.EntityTag> = Partial<InterpolableOrInterpolator<Entity.EntityByTag<Tag>>> | ((data: Entity.EntityByTag<Tag>) => Partial<InterpolableOrInterpolator<Entity.EntityByTag<Tag>>>);
|
|
78
|
+
export declare const resolveTarget: <Tag extends Entity.EntityTag>(target: Target<Tag>, current: Entity.EntityByTag<Tag>) => Record<string, number>;
|
|
22
79
|
export declare const startValues: (current: unknown, target: Record<string, number>, explicitFrom: Record<string, number>) => Record<string, number>;
|
|
23
80
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
81
|
+
* Interpolate numeric fields of an instance to `to` over `duration`,
|
|
82
|
+
* starting from whatever they currently are.
|
|
83
|
+
*
|
|
84
|
+
* @remarks
|
|
85
|
+
* This is the RAW layer: fields are addressed by their schema name, so
|
|
86
|
+
* `tweenTo` reaches everything the semantic animators don't cover —
|
|
87
|
+
* `radius`, `width`, `height`, `fontSize`, `strokeWidth`, a Camera's
|
|
88
|
+
* `focalLength`. Prefer {@link moveTo} / {@link fadeTo} where they apply;
|
|
89
|
+
* they move an entity as a unit, which a per-field tween cannot.
|
|
90
|
+
*
|
|
91
|
+
* Only the fields named in `to` are touched; the rest are left alone.
|
|
92
|
+
* `to` may also be a function of the current data, for a target computed
|
|
93
|
+
* at animation start. Colors are interpolable too, mixed through Lab by
|
|
94
|
+
* default.
|
|
95
|
+
*
|
|
96
|
+
* The final frame receives exactly `to` (for any easing with f(1) = 1), so
|
|
97
|
+
* a chain of tweens never accumulates drift. A field that has no current
|
|
98
|
+
* value — an optional one never set — is a loud defect rather than a
|
|
99
|
+
* silent NaN; pass an explicit origin with {@link tween} instead.
|
|
100
|
+
*
|
|
101
|
+
* @param to - The fields to animate and their target values.
|
|
102
|
+
* @param duration - How long, in scene time; rounded to whole frames at
|
|
103
|
+
* the runner's frame rate. A zero duration still takes one frame.
|
|
104
|
+
* @param timing - An easing name from `Timing.timingFunctions`, or your own
|
|
105
|
+
* `(t: number) => number`.
|
|
106
|
+
* @defaultValue `timing` — `"linear"`
|
|
107
|
+
* @returns The instance, so animators chain.
|
|
108
|
+
* @see {@link tween} to specify the starting value explicitly.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* Grow a circle's radius, then shrink it back with a different curve.
|
|
112
|
+
* ```typescript
|
|
113
|
+
* yield* dot.pipe(
|
|
114
|
+
* Motion.tweenTo({ radius: 40 }, "600 millis", "easeOutBack"),
|
|
115
|
+
* Motion.tweenTo({ radius: 4 }, "400 millis", "easeInQuad"),
|
|
116
|
+
* );
|
|
117
|
+
* ```
|
|
30
118
|
*/
|
|
31
|
-
export declare const tweenTo: (<
|
|
119
|
+
export declare const tweenTo: (<Tag extends Entity.EntityTag>(to: Target<Tag>, duration: Duration.Input, timing?: Timing.TimingInput) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>) & (<Tag extends Entity.EntityTag, E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>, to: Target<Tag>, duration: Duration.Input, timing?: Timing.TimingInput) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>);
|
|
32
120
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* cannot express
|
|
39
|
-
*
|
|
40
|
-
*
|
|
121
|
+
* The escape hatch: run `fn` once per frame with the eased progress and the
|
|
122
|
+
* entity's current data, and use whatever it returns as the new data.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* Reach for `drive` when fields must move TOGETHER in a way independent
|
|
126
|
+
* tweens cannot express. Tweening `x` and `y` separately gives you a
|
|
127
|
+
* straight line; a circular orbit needs both derived from one angle, and
|
|
128
|
+
* that coupling is what this provides. It is the primitive `Camera.orbit`
|
|
129
|
+
* and `Camera.dolly` are themselves built on.
|
|
130
|
+
*
|
|
131
|
+
* `fn` must be pure — it receives only `(t, data)` and returns new data, so
|
|
132
|
+
* a scene stays reproducible frame-for-frame. Reading a clock or a random
|
|
133
|
+
* number here is what breaks determinism. `t` is the EASED parameter, so a
|
|
134
|
+
* non-linear `timing` reshapes the pacing without `fn` knowing.
|
|
135
|
+
*
|
|
136
|
+
* The final frame receives exactly `t = 1` for any easing with f(1) = 1, so
|
|
137
|
+
* the motion lands precisely on whatever `fn(1, …)` computes.
|
|
138
|
+
*
|
|
139
|
+
* @param duration - How long, in scene time.
|
|
140
|
+
* @param timing - An easing name or function; applied to `t` before `fn`
|
|
141
|
+
* sees it.
|
|
142
|
+
* @param fn - Pure `(t, data) => data`, called once per frame.
|
|
143
|
+
* @returns The instance, so animators chain.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* Sweep a dot around a circle — one angle driving both axes, which two
|
|
147
|
+
* independent tweens could not do.
|
|
148
|
+
* ```typescript
|
|
149
|
+
* yield* dot.pipe(
|
|
150
|
+
* Motion.drive("2 seconds", "linear", (t, data) => ({
|
|
151
|
+
* ...data,
|
|
152
|
+
* position: Entity.vec3({
|
|
153
|
+
* x: 250 + Math.cos(t * 2 * Math.PI) * 120,
|
|
154
|
+
* y: 150 + Math.sin(t * 2 * Math.PI) * 120,
|
|
155
|
+
* }),
|
|
156
|
+
* })),
|
|
157
|
+
* );
|
|
158
|
+
* ```
|
|
41
159
|
*/
|
|
42
|
-
export declare const drive: (<
|
|
160
|
+
export declare const drive: (<Tag extends Entity.EntityTag>(duration: Duration.Input, timing: Timing.TimingInput, fn: (t: number, data: Entity.EntityByTag<Tag>) => Entity.EntityByTag<Tag>) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>) & (<Tag extends Entity.EntityTag, E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>, duration: Duration.Input, timing: Timing.TimingInput, fn: (t: number, data: Entity.EntityByTag<Tag>) => Entity.EntityByTag<Tag>) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>);
|
|
43
161
|
/**
|
|
44
|
-
* Like
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
162
|
+
* Like {@link tweenTo}, but with an explicit starting value.
|
|
163
|
+
*
|
|
164
|
+
* @remarks
|
|
165
|
+
* Reach for this when the animation should not begin where the entity
|
|
166
|
+
* currently sits — a "flash in from nothing" that must start at 0 no matter
|
|
167
|
+
* what the last animation left behind, or a field with no current value at
|
|
168
|
+
* all (an optional one never set), which {@link tweenTo} rejects as a
|
|
169
|
+
* defect.
|
|
170
|
+
*
|
|
171
|
+
* Keys present in `to` but missing from `from` still start at the current
|
|
172
|
+
* data, so a partial origin is a targeted override rather than an
|
|
173
|
+
* all-or-nothing switch.
|
|
174
|
+
*
|
|
175
|
+
* @param from - Starting values; keys omitted here start at the current data.
|
|
176
|
+
* @param to - Target values — this key set defines what gets animated.
|
|
177
|
+
* @param duration - How long, in scene time.
|
|
178
|
+
* @param timing - An easing name or function.
|
|
179
|
+
* @defaultValue `timing` — `"linear"`
|
|
180
|
+
* @returns The instance, so animators chain.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* Pop a label in from zero size, regardless of its current `fontSize`.
|
|
184
|
+
* ```typescript
|
|
185
|
+
* yield* label.pipe(
|
|
186
|
+
* Motion.tween({ fontSize: 0 }, { fontSize: 32 }, "400 millis", "easeOutBack"),
|
|
187
|
+
* );
|
|
188
|
+
* ```
|
|
48
189
|
*/
|
|
49
|
-
export declare const tween: (<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
readonly
|
|
190
|
+
export declare const tween: (<Tag extends Entity.EntityTag>(from: Target<Tag>, to: Target<Tag>, duration: Duration.Input, timing?: Timing.TimingInput) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>) & (<Tag extends Entity.EntityTag, E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>, from: Target<Tag>, to: Target<Tag>, duration: Duration.Input, timing?: Timing.TimingInput) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>);
|
|
191
|
+
/** a 3D position as the semantic animators speak it */
|
|
192
|
+
export type Position = {
|
|
193
|
+
readonly x: number;
|
|
194
|
+
readonly y: number;
|
|
195
|
+
readonly z: number;
|
|
55
196
|
};
|
|
197
|
+
/** tags whose entity carries a position: every one of them */
|
|
198
|
+
type Positionable = Entity.TagsWith<"position">;
|
|
199
|
+
/** tags whose entity carries opacity: every paintable one (not Camera) */
|
|
200
|
+
type Fadeable = Entity.TagsWith<"opacity">;
|
|
201
|
+
/**
|
|
202
|
+
* Move an instance to a position over `duration`.
|
|
203
|
+
*
|
|
204
|
+
* @remarks
|
|
205
|
+
* The semantic counterpart to tweening x/y/z by hand, and what you should
|
|
206
|
+
* reach for whenever you want something to travel. Because every entity's
|
|
207
|
+
* geometry is expressed RELATIVE to its `position`, one `moveTo` translates
|
|
208
|
+
* the whole shape rigidly: a Line carries both endpoints, a Path its whole
|
|
209
|
+
* command list, a Group its entire subtree. There is no per-entity special
|
|
210
|
+
* casing to remember.
|
|
211
|
+
*
|
|
212
|
+
* Targets are partial — naming only `x` holds `y` and `z` at their current
|
|
213
|
+
* values, which is what makes single-axis motion read cleanly. `z` is depth:
|
|
214
|
+
* moving along it changes an entity's size and parallax under a perspective
|
|
215
|
+
* camera rather than just its sort order.
|
|
216
|
+
*
|
|
217
|
+
* Accepted for any entity that carries a position, which is all of them —
|
|
218
|
+
* the Camera included, so this is also how you fly the camera.
|
|
219
|
+
*
|
|
220
|
+
* @param to - Target position; omitted axes hold their current value.
|
|
221
|
+
* @param duration - How long, in scene time.
|
|
222
|
+
* @param timing - An easing name or function.
|
|
223
|
+
* @defaultValue `timing` — `"linear"`
|
|
224
|
+
* @returns The instance, so animators chain.
|
|
225
|
+
* @see {@link move} to start from an explicit position, and
|
|
226
|
+
* `Physics.springTo` for durationless motion with momentum.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* Slide right, then drop — the second leg holds the x reached by the first.
|
|
230
|
+
* ```typescript
|
|
231
|
+
* yield* box.pipe(
|
|
232
|
+
* Motion.moveTo({ x: 400 }, "1 second", "easeInOutCubic"),
|
|
233
|
+
* Motion.moveTo({ y: 250 }, "600 millis", "easeInQuad"),
|
|
234
|
+
* );
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export declare const moveTo: (<Tag extends Positionable>(to: Partial<Position>, duration: Duration.Input, timing?: Timing.TimingInput) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>) & (<Tag extends Positionable, E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>, to: Partial<Position>, duration: Duration.Input, timing?: Timing.TimingInput) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>);
|
|
238
|
+
/**
|
|
239
|
+
* Like {@link moveTo}, but starting from an explicit position.
|
|
240
|
+
*
|
|
241
|
+
* @remarks
|
|
242
|
+
* The entrance animator: an instance can sit at its final position in the
|
|
243
|
+
* scene tree and still fly in from off-screen, because the origin is stated
|
|
244
|
+
* rather than read. Both `from` and `to` are partial, and each fills its
|
|
245
|
+
* missing axes from the current position.
|
|
246
|
+
*
|
|
247
|
+
* @param from - Starting position; omitted axes start at the current value.
|
|
248
|
+
* @param to - Target position; omitted axes hold their current value.
|
|
249
|
+
* @param duration - How long, in scene time.
|
|
250
|
+
* @param timing - An easing name or function.
|
|
251
|
+
* @defaultValue `timing` — `"linear"`
|
|
252
|
+
* @returns The instance, so animators chain.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* Slide a title in from off the left edge to where it was declared.
|
|
256
|
+
* ```typescript
|
|
257
|
+
* yield* title.pipe(
|
|
258
|
+
* Motion.move({ x: -200 }, { x: 60 }, "700 millis", "easeOutCubic"),
|
|
259
|
+
* );
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
export declare const move: (<Tag extends Positionable>(from: Partial<Position>, to: Partial<Position>, duration: Duration.Input, timing?: Timing.TimingInput) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>) & (<Tag extends Positionable, E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>, from: Partial<Position>, to: Partial<Position>, duration: Duration.Input, timing?: Timing.TimingInput) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>);
|
|
56
263
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
264
|
+
* Fade an instance's `opacity` to `to` over `duration`.
|
|
265
|
+
*
|
|
266
|
+
* @remarks
|
|
267
|
+
* Opacity runs 0 (invisible) to 1 (fully opaque), and applies to the whole
|
|
268
|
+
* subtree: fading a Group fades everything inside it, which is the usual way
|
|
269
|
+
* to dissolve a composed element as one piece.
|
|
270
|
+
*
|
|
271
|
+
* Only entities that actually paint carry `opacity`, so this is statically
|
|
272
|
+
* gated — fading a Camera is a compile error naming the missing field, not a
|
|
273
|
+
* runtime surprise.
|
|
274
|
+
*
|
|
275
|
+
* @param to - Target opacity, 0 to 1.
|
|
276
|
+
* @param duration - How long, in scene time.
|
|
277
|
+
* @param timing - An easing name or function.
|
|
278
|
+
* @defaultValue `timing` — `"linear"`
|
|
279
|
+
* @returns The instance, so animators chain.
|
|
280
|
+
* @see {@link fade} to start from an explicit opacity.
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* Land, hold, then dissolve out.
|
|
284
|
+
* ```typescript
|
|
285
|
+
* yield* card.pipe(
|
|
286
|
+
* Motion.moveTo({ y: 100 }, "500 millis"),
|
|
287
|
+
* Motion.wait("1 second"),
|
|
288
|
+
* Motion.fadeTo(0, "400 millis"),
|
|
289
|
+
* );
|
|
290
|
+
* ```
|
|
62
291
|
*/
|
|
63
|
-
export declare const
|
|
64
|
-
/** Like `moveTo`, but from an explicit position (partials filled from current). */
|
|
65
|
-
export declare const move: (<Name extends string, Data extends Schema.Top, Traits extends Partial<Entity.EntityTraits<Data["Type"]>> & HasPosition<Data>>(from: Partial<Entity.Position>, to: Partial<Entity.Position>, duration: Duration.Input, timing?: Timing.TimingInput) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Name, Data, Traits, E, R>) => Effect.Effect<Instance.Instance<Name, Data, Traits>, E, R | Runner.Runner>) & (<Name extends string, Data extends Schema.Top, Traits extends Partial<Entity.EntityTraits<Data["Type"]>> & HasPosition<Data>, E = never, R = never>(instance: Instance.InstanceOrEffect<Name, Data, Traits, E, R>, from: Partial<Entity.Position>, to: Partial<Entity.Position>, duration: Duration.Input, timing?: Timing.TimingInput) => Effect.Effect<Instance.Instance<Name, Data, Traits>, E, R | Runner.Runner>);
|
|
292
|
+
export declare const fadeTo: (<Tag extends Fadeable>(to: number, duration: Duration.Input, timing?: Timing.TimingInput) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>) & (<Tag extends Fadeable, E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>);
|
|
66
293
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
294
|
+
* Like {@link fadeTo}, but starting from an explicit opacity.
|
|
295
|
+
*
|
|
296
|
+
* @remarks
|
|
297
|
+
* The fade-in idiom: entities are born fully opaque, so `fadeTo(1)` alone
|
|
298
|
+
* would be a no-op. Stating the origin is what makes an entrance possible
|
|
299
|
+
* without first writing `opacity: 0` at instantiate time.
|
|
300
|
+
*
|
|
301
|
+
* @param from - Starting opacity, 0 to 1.
|
|
302
|
+
* @param to - Target opacity, 0 to 1.
|
|
303
|
+
* @param duration - How long, in scene time.
|
|
304
|
+
* @param timing - An easing name or function.
|
|
305
|
+
* @defaultValue `timing` — `"linear"`
|
|
306
|
+
* @returns The instance, so animators chain.
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* Fade a caption in from nothing.
|
|
310
|
+
* ```typescript
|
|
311
|
+
* yield* caption.pipe(Motion.fade(0, 1, "500 millis"));
|
|
312
|
+
* ```
|
|
70
313
|
*/
|
|
71
|
-
export declare const
|
|
72
|
-
/** Like `fadeTo`, but from an explicit opacity. */
|
|
73
|
-
export declare const fade: (<Name extends string, Data extends Schema.Top, Traits extends Partial<Entity.EntityTraits<Data["Type"]>> & HasOpacity<Data>>(from: number, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Name, Data, Traits, E, R>) => Effect.Effect<Instance.Instance<Name, Data, Traits>, E, R | Runner.Runner>) & (<Name extends string, Data extends Schema.Top, Traits extends Partial<Entity.EntityTraits<Data["Type"]>> & HasOpacity<Data>, E = never, R = never>(instance: Instance.InstanceOrEffect<Name, Data, Traits, E, R>, from: number, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => Effect.Effect<Instance.Instance<Name, Data, Traits>, E, R | Runner.Runner>);
|
|
314
|
+
export declare const fade: (<Tag extends Fadeable>(from: number, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => <E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>) & (<Tag extends Fadeable, E = never, R = never>(instance: Instance.InstanceOrEffect<Tag, E, R>, from: number, to: number, duration: Duration.Input, timing?: Timing.TimingInput) => Effect.Effect<Instance.Instance<Tag>, E, R | Runner.Runner>);
|
|
74
315
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
316
|
+
* The dual nature of {@link wait}: an Effect you can yield, and a function
|
|
317
|
+
* you can drop into a `pipe` chain.
|
|
318
|
+
*
|
|
319
|
+
* @remarks
|
|
320
|
+
* Being callable is what lets a hold sit BETWEEN two animators in the same
|
|
321
|
+
* chain — it wraps the preceding step, waits after it completes, and passes
|
|
322
|
+
* that step's value (the instance) through untouched, so the chain keeps
|
|
323
|
+
* flowing.
|
|
78
324
|
*/
|
|
79
325
|
export interface Wait extends Effect.Effect<void, never, Runner.Runner> {
|
|
80
|
-
<
|
|
326
|
+
<Self extends Effect.Effect<any, any, any> | Instance.Instance>(self: Self): Self extends Instance.Instance ? Effect.Effect<Self, never, Runner.Runner> : Self extends Effect.Effect<infer A, infer E, infer R> ? Effect.Effect<A, E, R | Runner.Runner> : never;
|
|
81
327
|
}
|
|
82
328
|
/**
|
|
83
|
-
* Hold
|
|
84
|
-
*
|
|
329
|
+
* Hold for `duration` of scene time — the chainable sibling of
|
|
330
|
+
* `Scene.sleep`.
|
|
331
|
+
*
|
|
332
|
+
* @remarks
|
|
333
|
+
* This is frame time, not wall time: `Effect.sleep` would block on a real
|
|
334
|
+
* clock and make the scene non-deterministic, so `wait` advances whole
|
|
335
|
+
* frames at the runner's frame rate instead. The same hold produces the
|
|
336
|
+
* same frame count on every run and on every machine.
|
|
337
|
+
*
|
|
338
|
+
* Its value over `Scene.sleep` is placement — because it doubles as a pipe
|
|
339
|
+
* step, a beat can sit inside an animator chain without breaking it apart
|
|
340
|
+
* into separate statements.
|
|
341
|
+
*
|
|
342
|
+
* As a pipe step it wraps the PRECEDING step and passes its value through,
|
|
343
|
+
* so it sits anywhere in a chain — including first, where the step it wraps
|
|
344
|
+
* is the bare instance handle.
|
|
345
|
+
*
|
|
346
|
+
* @param duration - How long to hold, in scene time.
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* A hold between two animations, mid-chain — `card` flows through to the
|
|
350
|
+
* fade.
|
|
351
|
+
* ```typescript
|
|
352
|
+
* yield* card.pipe(
|
|
353
|
+
* Motion.moveTo({ y: 100 }, "500 millis"),
|
|
354
|
+
* Motion.wait("1 second"),
|
|
355
|
+
* Motion.fadeTo(0, "300 millis"),
|
|
356
|
+
* );
|
|
357
|
+
* ```
|
|
85
358
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
359
|
+
* @example
|
|
360
|
+
* Or yielded on its own, as a plain pause in the scene body.
|
|
361
|
+
* ```typescript
|
|
362
|
+
* yield* Motion.wait("2 seconds");
|
|
363
|
+
* ```
|
|
89
364
|
*/
|
|
90
365
|
export declare const wait: (duration: Duration.Input) => Wait;
|
|
91
366
|
export {};
|