@cardstack/choreo 0.0.0 → 0.1.0-unstable.1
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/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +55 -6
- package/addon-main.cjs +4 -0
- package/declarations/anchors.d.ts +26 -0
- package/declarations/anchors.d.ts.map +1 -0
- package/declarations/arming.d.ts +33 -0
- package/declarations/arming.d.ts.map +1 -0
- package/declarations/beacon.d.ts +9 -0
- package/declarations/beacon.d.ts.map +1 -0
- package/declarations/beacons.d.ts +23 -0
- package/declarations/beacons.d.ts.map +1 -0
- package/declarations/changeset.d.ts +46 -0
- package/declarations/changeset.d.ts.map +1 -0
- package/declarations/choreo.d.ts +261 -0
- package/declarations/choreo.d.ts.map +1 -0
- package/declarations/compile.d.ts +45 -0
- package/declarations/compile.d.ts.map +1 -0
- package/declarations/deliver.d.ts +44 -0
- package/declarations/deliver.d.ts.map +1 -0
- package/declarations/easings.d.ts +20 -0
- package/declarations/easings.d.ts.map +1 -0
- package/declarations/far.d.ts +33 -0
- package/declarations/far.d.ts.map +1 -0
- package/declarations/film/clip.d.ts +51 -0
- package/declarations/film/clip.d.ts.map +1 -0
- package/declarations/film/clips.d.ts +155 -0
- package/declarations/film/clips.d.ts.map +1 -0
- package/declarations/film/film.d.ts +1208 -0
- package/declarations/film/film.d.ts.map +1 -0
- package/declarations/film/graph/adjust.d.ts +112 -0
- package/declarations/film/graph/adjust.d.ts.map +1 -0
- package/declarations/film/graph/compile.d.ts +140 -0
- package/declarations/film/graph/compile.d.ts.map +1 -0
- package/declarations/film/graph/host.d.ts +67 -0
- package/declarations/film/graph/host.d.ts.map +1 -0
- package/declarations/film/graph/nodes.d.ts +287 -0
- package/declarations/film/graph/nodes.d.ts.map +1 -0
- package/declarations/film/index.d.ts +24 -0
- package/declarations/film/index.d.ts.map +1 -0
- package/declarations/film/joins.d.ts +143 -0
- package/declarations/film/joins.d.ts.map +1 -0
- package/declarations/film/math.d.ts +15 -0
- package/declarations/film/math.d.ts.map +1 -0
- package/declarations/film/overlays.d.ts +54 -0
- package/declarations/film/overlays.d.ts.map +1 -0
- package/declarations/film/picture.d.ts +104 -0
- package/declarations/film/picture.d.ts.map +1 -0
- package/declarations/film/plate.d.ts +37 -0
- package/declarations/film/plate.d.ts.map +1 -0
- package/declarations/film/player.d.ts +120 -0
- package/declarations/film/player.d.ts.map +1 -0
- package/declarations/film/rail.d.ts +40 -0
- package/declarations/film/rail.d.ts.map +1 -0
- package/declarations/film/schedule.d.ts +93 -0
- package/declarations/film/schedule.d.ts.map +1 -0
- package/declarations/film/seam.d.ts +38 -0
- package/declarations/film/seam.d.ts.map +1 -0
- package/declarations/film/titles.d.ts +37 -0
- package/declarations/film/titles.d.ts.map +1 -0
- package/declarations/film/types.d.ts +544 -0
- package/declarations/film/types.d.ts.map +1 -0
- package/declarations/film.d.ts +3 -0
- package/declarations/film.d.ts.map +1 -0
- package/declarations/gesture.d.ts +33 -0
- package/declarations/gesture.d.ts.map +1 -0
- package/declarations/index.d.ts +26 -0
- package/declarations/index.d.ts.map +1 -0
- package/declarations/measure.d.ts +29 -0
- package/declarations/measure.d.ts.map +1 -0
- package/declarations/path.d.ts +73 -0
- package/declarations/path.d.ts.map +1 -0
- package/declarations/registry.d.ts +46 -0
- package/declarations/registry.d.ts.map +1 -0
- package/declarations/run.d.ts +406 -0
- package/declarations/run.d.ts.map +1 -0
- package/declarations/space.d.ts +31 -0
- package/declarations/space.d.ts.map +1 -0
- package/declarations/steps.d.ts +479 -0
- package/declarations/steps.d.ts.map +1 -0
- package/declarations/test-support/index.d.ts +30 -0
- package/declarations/test-support/index.d.ts.map +1 -0
- package/declarations/types.d.ts +761 -0
- package/declarations/types.d.ts.map +1 -0
- package/dist/anchors.js +34 -0
- package/dist/anchors.js.map +1 -0
- package/dist/arming.js +120 -0
- package/dist/arming.js.map +1 -0
- package/dist/beacon.js +25 -0
- package/dist/beacon.js.map +1 -0
- package/dist/beacons.js +77 -0
- package/dist/beacons.js.map +1 -0
- package/dist/changeset.js +129 -0
- package/dist/changeset.js.map +1 -0
- package/dist/choreo.js +1026 -0
- package/dist/choreo.js.map +1 -0
- package/dist/compile.js +1403 -0
- package/dist/compile.js.map +1 -0
- package/dist/deliver.js +326 -0
- package/dist/deliver.js.map +1 -0
- package/dist/easings.js +39 -0
- package/dist/easings.js.map +1 -0
- package/dist/far.js +147 -0
- package/dist/far.js.map +1 -0
- package/dist/film/clip.js +100 -0
- package/dist/film/clip.js.map +1 -0
- package/dist/film/clips.js +147 -0
- package/dist/film/clips.js.map +1 -0
- package/dist/film/film.js +4424 -0
- package/dist/film/film.js.map +1 -0
- package/dist/film/graph/adjust.js +160 -0
- package/dist/film/graph/adjust.js.map +1 -0
- package/dist/film/graph/compile.js +225 -0
- package/dist/film/graph/compile.js.map +1 -0
- package/dist/film/graph/host.js +77 -0
- package/dist/film/graph/host.js.map +1 -0
- package/dist/film/graph/nodes.js +500 -0
- package/dist/film/graph/nodes.js.map +1 -0
- package/dist/film/index.js +18 -0
- package/dist/film/index.js.map +1 -0
- package/dist/film/joins.js +260 -0
- package/dist/film/joins.js.map +1 -0
- package/dist/film/math.js +49 -0
- package/dist/film/math.js.map +1 -0
- package/dist/film/overlays.js +66 -0
- package/dist/film/overlays.js.map +1 -0
- package/dist/film/picture.js +58 -0
- package/dist/film/picture.js.map +1 -0
- package/dist/film/plate.js +36 -0
- package/dist/film/plate.js.map +1 -0
- package/dist/film/player.js +79 -0
- package/dist/film/player.js.map +1 -0
- package/dist/film/rail.js +38 -0
- package/dist/film/rail.js.map +1 -0
- package/dist/film/schedule.js +238 -0
- package/dist/film/schedule.js.map +1 -0
- package/dist/film/seam.js +64 -0
- package/dist/film/seam.js.map +1 -0
- package/dist/film/titles.js +45 -0
- package/dist/film/titles.js.map +1 -0
- package/dist/film/types.js +2 -0
- package/dist/film/types.js.map +1 -0
- package/dist/film.js +18 -0
- package/dist/film.js.map +1 -0
- package/dist/gesture.js +111 -0
- package/dist/gesture.js.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/measure.js +99 -0
- package/dist/measure.js.map +1 -0
- package/dist/path.js +272 -0
- package/dist/path.js.map +1 -0
- package/dist/registry.js +55 -0
- package/dist/registry.js.map +1 -0
- package/dist/run.js +2423 -0
- package/dist/run.js.map +1 -0
- package/dist/space.js +57 -0
- package/dist/space.js.map +1 -0
- package/dist/steps.js +831 -0
- package/dist/steps.js.map +1 -0
- package/dist/test-support/index.js +150 -0
- package/dist/test-support/index.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +201 -6
package/dist/steps.js
ADDED
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { modifier } from 'ember-modifier';
|
|
3
|
+
import { at } from './anchors.js';
|
|
4
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
5
|
+
import { setComponentTemplate } from '@ember/component';
|
|
6
|
+
|
|
7
|
+
const providers = new WeakMap();
|
|
8
|
+
/** unnamed crossings still need a stable, unique inner anchor name */
|
|
9
|
+
let crossings = 0;
|
|
10
|
+
/** the steps directly inside an element, in document order; a nested <Choreo> is another region's */
|
|
11
|
+
function collect(el) {
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const child of Array.from(el.children)) {
|
|
14
|
+
if (child.hasAttribute('data-choreo')) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const provider = providers.get(child);
|
|
18
|
+
if (provider) {
|
|
19
|
+
out.push(provider.node());
|
|
20
|
+
} else {
|
|
21
|
+
out.push(...collect(child));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* args that are the step's own; every other named arg is a property to animate.
|
|
28
|
+
* `rotate` is NOT here although Move has a `@rotate` arg: on a Tween or
|
|
29
|
+
* Spring it is a property (Keynote's Spin), and Move never reads its props.
|
|
30
|
+
*/
|
|
31
|
+
const RESERVED = new Set(['align', 'at', 'by', 'delay', 'duration', 'ease', 'fill', 'from', 'name', 'of', 'order', 'path', 'read', 'repeat', 'repeatType', 'rest', 'shadow', 'size', 'space', 'spring', 'stagger', 'swap', 'to', 'times']);
|
|
32
|
+
/** the pre-seconds spellings, kept only to fail loudly with the new name */
|
|
33
|
+
const RENAMED = {
|
|
34
|
+
crossfade: '@swap',
|
|
35
|
+
ms: '@duration (in seconds)',
|
|
36
|
+
overlap: '@stagger (in seconds)'
|
|
37
|
+
};
|
|
38
|
+
function propsOf(args) {
|
|
39
|
+
const props = {};
|
|
40
|
+
for (const key of Object.keys(args)) {
|
|
41
|
+
if (args[key] === undefined) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (RENAMED[key]) {
|
|
45
|
+
throw new Error(`choreo: @${key} was renamed — use ${RENAMED[key]}`);
|
|
46
|
+
}
|
|
47
|
+
if (!RESERVED.has(key)) {
|
|
48
|
+
props[key] = args[key];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return props;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The template speaks seconds, as Motion does; the compiler's clock is ms.
|
|
55
|
+
* A composite step writes node literals, which are BELOW that boundary —
|
|
56
|
+
* so it converts its own seconds args here, and `undefined` stays
|
|
57
|
+
* `undefined` so an unset arg still means "take the default".
|
|
58
|
+
*/
|
|
59
|
+
function toMs(seconds) {
|
|
60
|
+
// the conditional return type is the whole point — a required `ms` field
|
|
61
|
+
// takes toMs(x) without a null check when x is a number — and TypeScript
|
|
62
|
+
// cannot verify a conditional return from inside, so the cast is the
|
|
63
|
+
// price of stating it
|
|
64
|
+
return seconds === undefined ? undefined : seconds * 1000;
|
|
65
|
+
}
|
|
66
|
+
const msOf = toMs;
|
|
67
|
+
/** the args every step shares — a composite step extends this */
|
|
68
|
+
|
|
69
|
+
/** …and the args of a step that names sprites */
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The base every step is built on, and the public seam for a COMPOSITE
|
|
73
|
+
* step — a new word in the timeline that expands into the built-in
|
|
74
|
+
* vocabulary. `c.Crossing` is one, and is written in nothing but this.
|
|
75
|
+
*
|
|
76
|
+
* Subclass it, implement `node()`, and render the component anywhere
|
|
77
|
+
* inside a `<Choreo>`: the region reads the tree back in document order
|
|
78
|
+
* and your node takes its place in it.
|
|
79
|
+
*
|
|
80
|
+
* ```gts
|
|
81
|
+
* export class Reveal extends StepComponent<StepArgs & { rise?: number }> {
|
|
82
|
+
* node(): TimelineNode {
|
|
83
|
+
* const { of, name, rise = 12 } = this.args;
|
|
84
|
+
* return {
|
|
85
|
+
* at: this.args.at,
|
|
86
|
+
* children: [
|
|
87
|
+
* { kind: 'tween', generic: true, of, ms: 240, props: { opacity: [0, 1] } },
|
|
88
|
+
* { kind: 'tween', generic: true, of, ms: 240, props: { y: [rise, 0] } },
|
|
89
|
+
* ],
|
|
90
|
+
* kind: 'parallel',
|
|
91
|
+
* name,
|
|
92
|
+
* };
|
|
93
|
+
* }
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* **`node()` is called on every pass**, and its result is fingerprinted to
|
|
98
|
+
* decide whether an edited timeline replays the run. So it must be pure
|
|
99
|
+
* and cheap: no measurement, no DOM writes, no tracked writes (a tracked
|
|
100
|
+
* write re-renders, which replays the pass, which calls `node()`). And
|
|
101
|
+
* because functions in the tree are compared by identity, a `node()` that
|
|
102
|
+
* allocates a fresh closure per call declares an edit on every pass —
|
|
103
|
+
* hoist property functions to module scope.
|
|
104
|
+
*
|
|
105
|
+
* Two conventions worth keeping: mark children `generic: true` so a
|
|
106
|
+
* sibling step can claim a sprite away from your defaults (the yield
|
|
107
|
+
* rule, §4.7), and derive any inner `name` from your own `@name` so two
|
|
108
|
+
* of your step in one timeline do not collide.
|
|
109
|
+
*/
|
|
110
|
+
class StepComponent extends Component {
|
|
111
|
+
mark = modifier(el => {
|
|
112
|
+
providers.set(el, this);
|
|
113
|
+
return () => providers.delete(el);
|
|
114
|
+
});
|
|
115
|
+
static {
|
|
116
|
+
setComponentTemplate(precompileTemplate("<span hidden data-choreo-step {{this.mark}}></span>", {
|
|
117
|
+
strictMode: true
|
|
118
|
+
}), this);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
class Tween extends StepComponent {
|
|
122
|
+
node() {
|
|
123
|
+
const {
|
|
124
|
+
of,
|
|
125
|
+
by,
|
|
126
|
+
duration,
|
|
127
|
+
ease,
|
|
128
|
+
delay,
|
|
129
|
+
order,
|
|
130
|
+
repeat,
|
|
131
|
+
repeatType,
|
|
132
|
+
times
|
|
133
|
+
} = this.args;
|
|
134
|
+
return {
|
|
135
|
+
at: this.args.at,
|
|
136
|
+
by,
|
|
137
|
+
delay: msOf(delay),
|
|
138
|
+
ease,
|
|
139
|
+
kind: 'tween',
|
|
140
|
+
name: this.args.name,
|
|
141
|
+
ms: duration * 1000,
|
|
142
|
+
of,
|
|
143
|
+
order,
|
|
144
|
+
props: propsOf(this.args),
|
|
145
|
+
repeat,
|
|
146
|
+
repeatType,
|
|
147
|
+
times,
|
|
148
|
+
stagger: msOf(this.args.stagger)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
class Spring extends StepComponent {
|
|
153
|
+
node() {
|
|
154
|
+
const {
|
|
155
|
+
of,
|
|
156
|
+
by,
|
|
157
|
+
spring,
|
|
158
|
+
delay,
|
|
159
|
+
order
|
|
160
|
+
} = this.args;
|
|
161
|
+
return {
|
|
162
|
+
at: this.args.at,
|
|
163
|
+
by,
|
|
164
|
+
delay: msOf(delay),
|
|
165
|
+
kind: 'spring',
|
|
166
|
+
name: this.args.name,
|
|
167
|
+
of,
|
|
168
|
+
order,
|
|
169
|
+
props: propsOf(this.args),
|
|
170
|
+
spring,
|
|
171
|
+
stagger: msOf(this.args.stagger)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
class Move extends StepComponent {
|
|
176
|
+
node() {
|
|
177
|
+
const {
|
|
178
|
+
of,
|
|
179
|
+
duration,
|
|
180
|
+
ease,
|
|
181
|
+
delay,
|
|
182
|
+
path,
|
|
183
|
+
rotate,
|
|
184
|
+
space,
|
|
185
|
+
spring,
|
|
186
|
+
size,
|
|
187
|
+
swap,
|
|
188
|
+
from,
|
|
189
|
+
to,
|
|
190
|
+
stagger
|
|
191
|
+
} = this.args;
|
|
192
|
+
propsOf(this.args); // no properties — evaluated for the renamed-arg errors
|
|
193
|
+
return {
|
|
194
|
+
at: this.args.at,
|
|
195
|
+
delay: msOf(delay),
|
|
196
|
+
ease,
|
|
197
|
+
from,
|
|
198
|
+
kind: 'move',
|
|
199
|
+
name: this.args.name,
|
|
200
|
+
ms: msOf(duration),
|
|
201
|
+
of,
|
|
202
|
+
path,
|
|
203
|
+
rotate,
|
|
204
|
+
size,
|
|
205
|
+
space,
|
|
206
|
+
spring,
|
|
207
|
+
stagger: msOf(stagger),
|
|
208
|
+
swap,
|
|
209
|
+
to
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
class Hold extends StepComponent {
|
|
214
|
+
node() {
|
|
215
|
+
const {
|
|
216
|
+
of,
|
|
217
|
+
duration,
|
|
218
|
+
delay,
|
|
219
|
+
fill,
|
|
220
|
+
stagger
|
|
221
|
+
} = this.args;
|
|
222
|
+
return {
|
|
223
|
+
at: this.args.at,
|
|
224
|
+
delay: msOf(delay),
|
|
225
|
+
fill,
|
|
226
|
+
kind: 'hold',
|
|
227
|
+
name: this.args.name,
|
|
228
|
+
ms: msOf(duration),
|
|
229
|
+
of,
|
|
230
|
+
props: propsOf(this.args),
|
|
231
|
+
stagger: msOf(stagger)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* `<c.Wait />` — a hole in a sequence. It has no subject: `@of` is accepted
|
|
237
|
+
* (a wait can be laddered across sprites with `@stagger`) but never needed.
|
|
238
|
+
*/
|
|
239
|
+
class Wait extends StepComponent {
|
|
240
|
+
node() {
|
|
241
|
+
const {
|
|
242
|
+
of,
|
|
243
|
+
duration,
|
|
244
|
+
delay,
|
|
245
|
+
stagger
|
|
246
|
+
} = this.args;
|
|
247
|
+
return {
|
|
248
|
+
at: this.args.at,
|
|
249
|
+
delay: msOf(delay),
|
|
250
|
+
kind: 'wait',
|
|
251
|
+
name: this.args.name,
|
|
252
|
+
ms: duration * 1000,
|
|
253
|
+
of,
|
|
254
|
+
stagger: msOf(stagger)
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* `<c.Perform />` — a semantic command on the timeline (§C4). It occupies
|
|
260
|
+
* an instant, positioned like any step (sequence order, `@at`, `@delay`),
|
|
261
|
+
* and carries `@action` (required), `@target` and `@payload` to the
|
|
262
|
+
* region's dispatcher (`<Choreo @onPerform>`). The fold's law: forward
|
|
263
|
+
* playback dispatches it once as the clock crosses it; a seek past it
|
|
264
|
+
* includes it; a seek before it resets the host and replays the remaining
|
|
265
|
+
* prefix. Commands are idempotent statements of state — `lightbox.open`,
|
|
266
|
+
* never `lightbox.toggle`.
|
|
267
|
+
*/
|
|
268
|
+
class Perform extends StepComponent {
|
|
269
|
+
node() {
|
|
270
|
+
const {
|
|
271
|
+
action,
|
|
272
|
+
delay,
|
|
273
|
+
payload,
|
|
274
|
+
target
|
|
275
|
+
} = this.args;
|
|
276
|
+
return {
|
|
277
|
+
action,
|
|
278
|
+
at: this.args.at,
|
|
279
|
+
delay: msOf(delay),
|
|
280
|
+
kind: 'perform',
|
|
281
|
+
name: this.args.name,
|
|
282
|
+
payload,
|
|
283
|
+
target
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* `<c.Scroll />` — animate the sprite's scroll container so the sprite lands
|
|
289
|
+
* at `@align`; occupies the sequence like any step (§6.1).
|
|
290
|
+
*/ /**
|
|
291
|
+
* `<c.Attach @region @duration />` — drive another region's run over a
|
|
292
|
+
* window of this one's clock. Positioned like any step (sequence order,
|
|
293
|
+
* `@at`, `@delay`); `@in` and `@rate` map the window onto the child's own
|
|
294
|
+
* seconds; `@end` says what happens past it; `@exact` refuses a child
|
|
295
|
+
* that cannot be driven exactly (a spring, a follow).
|
|
296
|
+
*/
|
|
297
|
+
class Attach extends StepComponent {
|
|
298
|
+
node() {
|
|
299
|
+
const {
|
|
300
|
+
delay,
|
|
301
|
+
duration,
|
|
302
|
+
end,
|
|
303
|
+
exact,
|
|
304
|
+
rate,
|
|
305
|
+
region
|
|
306
|
+
} = this.args;
|
|
307
|
+
return {
|
|
308
|
+
at: this.args.at,
|
|
309
|
+
delay: msOf(delay),
|
|
310
|
+
end,
|
|
311
|
+
exact,
|
|
312
|
+
in: this.args.in,
|
|
313
|
+
kind: 'attach',
|
|
314
|
+
ms: duration * 1000,
|
|
315
|
+
name: this.args.name,
|
|
316
|
+
rate,
|
|
317
|
+
region
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
class Scroll extends StepComponent {
|
|
322
|
+
node() {
|
|
323
|
+
const {
|
|
324
|
+
of,
|
|
325
|
+
align,
|
|
326
|
+
duration,
|
|
327
|
+
delay,
|
|
328
|
+
stagger
|
|
329
|
+
} = this.args;
|
|
330
|
+
return {
|
|
331
|
+
align,
|
|
332
|
+
at: this.args.at,
|
|
333
|
+
delay: msOf(delay),
|
|
334
|
+
kind: 'scroll',
|
|
335
|
+
ms: msOf(duration),
|
|
336
|
+
name: this.args.name,
|
|
337
|
+
of,
|
|
338
|
+
stagger: msOf(stagger)
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* `<c.Raise />` — promote the sprites to the region's elevated layer for the
|
|
344
|
+
* span of its block (or `@duration`): above every stacking context and clip
|
|
345
|
+
* in the region, with measured continuity both ways. `@shadow` casts on the
|
|
346
|
+
* layer below (§6.3).
|
|
347
|
+
*/
|
|
348
|
+
class Raise extends StepComponent {
|
|
349
|
+
node() {
|
|
350
|
+
const {
|
|
351
|
+
of,
|
|
352
|
+
duration,
|
|
353
|
+
delay,
|
|
354
|
+
shadow,
|
|
355
|
+
stagger
|
|
356
|
+
} = this.args;
|
|
357
|
+
return {
|
|
358
|
+
at: this.args.at,
|
|
359
|
+
delay: msOf(delay),
|
|
360
|
+
kind: 'raise',
|
|
361
|
+
ms: msOf(duration),
|
|
362
|
+
name: this.args.name,
|
|
363
|
+
of,
|
|
364
|
+
shadow,
|
|
365
|
+
stagger: msOf(stagger)
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* `<c.Camera />` — the region's frame as a step (§6.3): `@zoom` / `@x` /
|
|
371
|
+
* `@y` animate the scene, `@origin` aims the zoom at a sprite, `@steady`
|
|
372
|
+
* names sprites that keep their size against it (damped by default).
|
|
373
|
+
* `@fit` is the other, more common shape — dive on this sprite and CENTRE
|
|
374
|
+
* it, zoom and pan computed from rest-layout geometry (`@margin` sets the
|
|
375
|
+
* share of the frame it fills); pass `null` to fit nothing: back to rest.
|
|
376
|
+
*/
|
|
377
|
+
class Camera extends StepComponent {
|
|
378
|
+
node() {
|
|
379
|
+
const {
|
|
380
|
+
duration,
|
|
381
|
+
ease,
|
|
382
|
+
delay,
|
|
383
|
+
fit,
|
|
384
|
+
margin,
|
|
385
|
+
origin,
|
|
386
|
+
spring,
|
|
387
|
+
steady
|
|
388
|
+
} = this.args;
|
|
389
|
+
const {
|
|
390
|
+
x,
|
|
391
|
+
y,
|
|
392
|
+
zoom
|
|
393
|
+
} = this.args;
|
|
394
|
+
return {
|
|
395
|
+
at: this.args.at,
|
|
396
|
+
delay: msOf(delay),
|
|
397
|
+
ease,
|
|
398
|
+
fit,
|
|
399
|
+
kind: 'camera',
|
|
400
|
+
margin,
|
|
401
|
+
ms: msOf(duration),
|
|
402
|
+
name: this.args.name,
|
|
403
|
+
of: this.args.of ?? {},
|
|
404
|
+
origin,
|
|
405
|
+
spring,
|
|
406
|
+
steady,
|
|
407
|
+
x,
|
|
408
|
+
y,
|
|
409
|
+
zoom
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* `<c.Camera3D @yaw={{-24}} @pitch={{-6}} @dolly={{0.9}} />` — the shot,
|
|
415
|
+
* for a scene Choreo is not the one drawing.
|
|
416
|
+
*
|
|
417
|
+
* `c.Camera` moves the region's own frame, which is a 2D transform on real
|
|
418
|
+
* DOM. A 3D scene has no such frame: its camera belongs to whatever is
|
|
419
|
+
* rendering it. So this step carries the POSE and nothing else — yaw and
|
|
420
|
+
* pitch in degrees, dolly as a multiple of the host's own framing — and
|
|
421
|
+
* the region hands it to `@onCamera3D` every frame it changes. The host
|
|
422
|
+
* applies it to three.js, to a CSS 3D stage, to anything that takes three
|
|
423
|
+
* numbers.
|
|
424
|
+
*
|
|
425
|
+
* What Choreo keeps is the part it is actually good at: the pose is a pure
|
|
426
|
+
* function of the clock, so a scrub lands the shot exactly where playing
|
|
427
|
+
* there would, and a preset expands into this one seekable cue rather than
|
|
428
|
+
* a second scheduler.
|
|
429
|
+
*
|
|
430
|
+
* `@by` makes it relative — added to the pose in force when the cue
|
|
431
|
+
* starts, the way `Pan` and `SlowZoom` are relative — so a drift composes
|
|
432
|
+
* with whatever shot preceded it.
|
|
433
|
+
*/
|
|
434
|
+
class Camera3D extends StepComponent {
|
|
435
|
+
node() {
|
|
436
|
+
const {
|
|
437
|
+
by,
|
|
438
|
+
delay,
|
|
439
|
+
dolly,
|
|
440
|
+
duration,
|
|
441
|
+
ease,
|
|
442
|
+
look,
|
|
443
|
+
pitch,
|
|
444
|
+
settle,
|
|
445
|
+
spring,
|
|
446
|
+
tension,
|
|
447
|
+
through,
|
|
448
|
+
x,
|
|
449
|
+
y,
|
|
450
|
+
yaw
|
|
451
|
+
} = this.args;
|
|
452
|
+
return {
|
|
453
|
+
at: this.args.at,
|
|
454
|
+
by,
|
|
455
|
+
delay: msOf(delay),
|
|
456
|
+
dolly,
|
|
457
|
+
ease,
|
|
458
|
+
kind: 'camera3d',
|
|
459
|
+
look,
|
|
460
|
+
ms: msOf(duration),
|
|
461
|
+
name: this.args.name,
|
|
462
|
+
of: this.args.of ?? {},
|
|
463
|
+
pitch,
|
|
464
|
+
settle,
|
|
465
|
+
spring,
|
|
466
|
+
tension,
|
|
467
|
+
through,
|
|
468
|
+
x,
|
|
469
|
+
y,
|
|
470
|
+
yaw
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
/* ---- the direction vocabulary (notes/choreo-composition.md C5) ----
|
|
475
|
+
Presets expand into the SAME seekable camera cue — sugar, never a new
|
|
476
|
+
runtime primitive. Frame and Aim are absolute (computed from measured
|
|
477
|
+
geometry at compile); Pan and SlowZoom are RELATIVE, resolved against
|
|
478
|
+
the pose in force when the cue starts, so they compose with whatever
|
|
479
|
+
shot preceded them and reconstruct under random access like any cue. */ /**
|
|
480
|
+
* `<c.Frame @of={{c.id 'hero'}} @padding={{0.8}} />` — fit-and-centre the
|
|
481
|
+
* target: the fit camera under its editorial name. `@padding` is the
|
|
482
|
+
* share of the frame the target fills on whichever axis fits first.
|
|
483
|
+
*/
|
|
484
|
+
class Frame extends StepComponent {
|
|
485
|
+
node() {
|
|
486
|
+
const {
|
|
487
|
+
duration,
|
|
488
|
+
ease,
|
|
489
|
+
delay,
|
|
490
|
+
of,
|
|
491
|
+
padding,
|
|
492
|
+
spring,
|
|
493
|
+
steady
|
|
494
|
+
} = this.args;
|
|
495
|
+
return {
|
|
496
|
+
at: this.args.at,
|
|
497
|
+
delay: msOf(delay),
|
|
498
|
+
ease,
|
|
499
|
+
fit: of,
|
|
500
|
+
kind: 'camera',
|
|
501
|
+
margin: padding,
|
|
502
|
+
ms: msOf(duration),
|
|
503
|
+
name: this.args.name,
|
|
504
|
+
of: {},
|
|
505
|
+
spring,
|
|
506
|
+
steady
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* `<c.Aim @of={{c.id 'hero'}} />` — recentre the picture on the target
|
|
512
|
+
* with the zoom HELD: the reframe that does not change magnification.
|
|
513
|
+
*/
|
|
514
|
+
class Aim extends StepComponent {
|
|
515
|
+
node() {
|
|
516
|
+
const {
|
|
517
|
+
duration,
|
|
518
|
+
ease,
|
|
519
|
+
delay,
|
|
520
|
+
of,
|
|
521
|
+
spring,
|
|
522
|
+
steady
|
|
523
|
+
} = this.args;
|
|
524
|
+
return {
|
|
525
|
+
aim: of,
|
|
526
|
+
at: this.args.at,
|
|
527
|
+
delay: msOf(delay),
|
|
528
|
+
ease,
|
|
529
|
+
kind: 'camera',
|
|
530
|
+
ms: msOf(duration),
|
|
531
|
+
name: this.args.name,
|
|
532
|
+
of: {},
|
|
533
|
+
spring,
|
|
534
|
+
steady
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* `<c.Pan @x={{40}} @y={{-20}} />` — shift the picture by exactly this
|
|
540
|
+
* many pixels from wherever it stands. Relative on purpose: a pan after
|
|
541
|
+
* any shot means "from here", not "to there".
|
|
542
|
+
*/
|
|
543
|
+
class Pan extends StepComponent {
|
|
544
|
+
node() {
|
|
545
|
+
const {
|
|
546
|
+
duration,
|
|
547
|
+
ease,
|
|
548
|
+
delay,
|
|
549
|
+
spring,
|
|
550
|
+
steady,
|
|
551
|
+
x,
|
|
552
|
+
y
|
|
553
|
+
} = this.args;
|
|
554
|
+
return {
|
|
555
|
+
at: this.args.at,
|
|
556
|
+
delay: msOf(delay),
|
|
557
|
+
ease,
|
|
558
|
+
kind: 'camera',
|
|
559
|
+
ms: msOf(duration),
|
|
560
|
+
name: this.args.name,
|
|
561
|
+
of: {},
|
|
562
|
+
panBy: {
|
|
563
|
+
x,
|
|
564
|
+
y
|
|
565
|
+
},
|
|
566
|
+
spring,
|
|
567
|
+
steady
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* `<c.SlowZoom @by={{1.05}} @duration={{2}} />` — multiply the zoom in
|
|
573
|
+
* force: the push-in (or, below 1, the pull-back) that gives a hold its
|
|
574
|
+
* life. The aim in force is kept, so it pushes toward what the previous
|
|
575
|
+
* shot was looking at.
|
|
576
|
+
*/
|
|
577
|
+
class SlowZoom extends StepComponent {
|
|
578
|
+
node() {
|
|
579
|
+
const {
|
|
580
|
+
by,
|
|
581
|
+
duration,
|
|
582
|
+
ease,
|
|
583
|
+
delay,
|
|
584
|
+
spring,
|
|
585
|
+
steady
|
|
586
|
+
} = this.args;
|
|
587
|
+
return {
|
|
588
|
+
at: this.args.at,
|
|
589
|
+
delay: msOf(delay),
|
|
590
|
+
ease,
|
|
591
|
+
kind: 'camera',
|
|
592
|
+
ms: msOf(duration),
|
|
593
|
+
name: this.args.name,
|
|
594
|
+
of: {},
|
|
595
|
+
spring,
|
|
596
|
+
steady,
|
|
597
|
+
zoomBy: by
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* `<c.Tether />` — geometry continuously derived from sprites (§6.1):
|
|
603
|
+
* `@path` receives both endpoints' region-relative boxes every frame and
|
|
604
|
+
* returns the path data the wire draws.
|
|
605
|
+
*/
|
|
606
|
+
class Tether extends StepComponent {
|
|
607
|
+
node() {
|
|
608
|
+
const {
|
|
609
|
+
duration,
|
|
610
|
+
delay,
|
|
611
|
+
from,
|
|
612
|
+
path,
|
|
613
|
+
to
|
|
614
|
+
} = this.args;
|
|
615
|
+
return {
|
|
616
|
+
at: this.args.at,
|
|
617
|
+
delay: msOf(delay),
|
|
618
|
+
from,
|
|
619
|
+
kind: 'tether',
|
|
620
|
+
ms: msOf(duration),
|
|
621
|
+
name: this.args.name,
|
|
622
|
+
of: this.args.of,
|
|
623
|
+
path,
|
|
624
|
+
to
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* `<c.Crossing />` — the canned scene crossing (§4.7): what only the old
|
|
630
|
+
* scene had fades as the flight lifts off, everything paired flies (skins
|
|
631
|
+
* swapping per `@swap`), and what only the new scene has fades in near the
|
|
632
|
+
* settle. One step in the template; a whole overlapped score on the clock —
|
|
633
|
+
* Keynote's Magic Move does not wait for the dissolve to finish before the
|
|
634
|
+
* movers leave, and a crossing that does reads as three acts, not one.
|
|
635
|
+
*
|
|
636
|
+
* It is also the reference COMPOSITE step, and deliberately written in
|
|
637
|
+
* nothing but the public seam (`StepComponent`, `toMs`, `at`, and node
|
|
638
|
+
* literals): if this needed a privilege an author could not have, the
|
|
639
|
+
* contract on `StepComponent` would be a lie.
|
|
640
|
+
*/
|
|
641
|
+
class Crossing extends StepComponent {
|
|
642
|
+
/**
|
|
643
|
+
* The flight is addressable from outside — `{{at 'page:flight' 0.5}}`
|
|
644
|
+
* for a step that wants to ride the move itself. Derived from this
|
|
645
|
+
* crossing's own `@name` rather than fixed, so two crossings in one
|
|
646
|
+
* timeline do not collide over it; unnamed, it takes a per-instance
|
|
647
|
+
* number, which is stable for the life of the component and so does not
|
|
648
|
+
* disturb the tree fingerprint between passes.
|
|
649
|
+
*/
|
|
650
|
+
flightName = `${this.args.name ?? `crossing-${++crossings}`}:flight`;
|
|
651
|
+
node() {
|
|
652
|
+
const {
|
|
653
|
+
arrive = 0.22,
|
|
654
|
+
duration,
|
|
655
|
+
ease,
|
|
656
|
+
leave = 0.18,
|
|
657
|
+
overlap = 0.7,
|
|
658
|
+
size = 'crop',
|
|
659
|
+
spring,
|
|
660
|
+
swap
|
|
661
|
+
} = this.args;
|
|
662
|
+
const FLIGHT = this.flightName;
|
|
663
|
+
return {
|
|
664
|
+
at: this.args.at,
|
|
665
|
+
delay: toMs(this.args.delay),
|
|
666
|
+
name: this.args.name,
|
|
667
|
+
children: [{
|
|
668
|
+
generic: true,
|
|
669
|
+
kind: 'tween',
|
|
670
|
+
ms: toMs(leave),
|
|
671
|
+
of: {
|
|
672
|
+
onstage: true,
|
|
673
|
+
type: 'departed'
|
|
674
|
+
},
|
|
675
|
+
props: {
|
|
676
|
+
opacity: 0
|
|
677
|
+
}
|
|
678
|
+
}, {
|
|
679
|
+
ease,
|
|
680
|
+
generic: true,
|
|
681
|
+
kind: 'move',
|
|
682
|
+
ms: toMs(duration),
|
|
683
|
+
name: FLIGHT,
|
|
684
|
+
of: {
|
|
685
|
+
type: 'received'
|
|
686
|
+
},
|
|
687
|
+
// iOS's rule: uniform scale matched by cover, the aspect mismatch
|
|
688
|
+
// cropped by the interpolating window — never a stretch, and
|
|
689
|
+
// never layout (a receiver animating real width/height reflows
|
|
690
|
+
// its whole row for the flight). Overridable; the default stays
|
|
691
|
+
// crop so a crossing in a grid cannot stretch the row.
|
|
692
|
+
size,
|
|
693
|
+
spring,
|
|
694
|
+
swap
|
|
695
|
+
}, {
|
|
696
|
+
generic: true,
|
|
697
|
+
kind: 'hold',
|
|
698
|
+
of: {
|
|
699
|
+
type: 'received'
|
|
700
|
+
},
|
|
701
|
+
props: {
|
|
702
|
+
zIndex: 2
|
|
703
|
+
}
|
|
704
|
+
}, {
|
|
705
|
+
at: at(FLIGHT, overlap),
|
|
706
|
+
generic: true,
|
|
707
|
+
kind: 'tween',
|
|
708
|
+
ms: toMs(arrive),
|
|
709
|
+
of: {
|
|
710
|
+
onstage: true,
|
|
711
|
+
type: 'inserted'
|
|
712
|
+
},
|
|
713
|
+
props: {
|
|
714
|
+
opacity: [0, 1]
|
|
715
|
+
}
|
|
716
|
+
}],
|
|
717
|
+
kind: 'parallel'
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* `<c.Follow />` — a value DERIVED from the scene rather than interpolated
|
|
723
|
+
* between two keyframes (§4.10). `@to` names what to read; `@read` gets
|
|
724
|
+
* the pass's measurements every frame — each source's resting boxes
|
|
725
|
+
* (`from`, `to`) and its composed position this frame (`now`), plus the
|
|
726
|
+
* follower's own `rest` — and returns the properties to write; `@rest`
|
|
727
|
+
* says what those properties are when nothing is driving them, so a
|
|
728
|
+
* measure pass can put the element back.
|
|
729
|
+
*
|
|
730
|
+
* ```gts
|
|
731
|
+
* <c.Follow @of={{c.id 'badge'}} @to={{c.id 'card'}}
|
|
732
|
+
* @read={{corner}} @rest={{hash x=0 y=0}} />
|
|
733
|
+
* ```
|
|
734
|
+
*
|
|
735
|
+
* `@read` never sees the live page. Everything it is handed was measured
|
|
736
|
+
* by the pass or composed from the run's own values, so it cannot read
|
|
737
|
+
* back what it wrote last frame and it cannot force a style recalculation
|
|
738
|
+
* mid-move — pure by construction, and correct under interruption because
|
|
739
|
+
* a replacement pass re-measures (docs/postmortem-follow.md).
|
|
740
|
+
*
|
|
741
|
+
* Three things it is not: it is not accelerated (a derived value is
|
|
742
|
+
* computed on the main thread, every frame — the price, and the same one
|
|
743
|
+
* `c.Tether` pays); it may not write layout, only transform, opacity and
|
|
744
|
+
* filter; and `@read` must still be a pure function of its context,
|
|
745
|
+
* because the run is scrubbable in both directions and a value with
|
|
746
|
+
* memory could not be sought back to.
|
|
747
|
+
*/
|
|
748
|
+
class Follow extends StepComponent {
|
|
749
|
+
node() {
|
|
750
|
+
const {
|
|
751
|
+
duration,
|
|
752
|
+
of,
|
|
753
|
+
read,
|
|
754
|
+
rest,
|
|
755
|
+
to
|
|
756
|
+
} = this.args;
|
|
757
|
+
return {
|
|
758
|
+
at: this.args.at,
|
|
759
|
+
delay: msOf(this.args.delay),
|
|
760
|
+
kind: 'follow',
|
|
761
|
+
ms: msOf(duration),
|
|
762
|
+
name: this.args.name,
|
|
763
|
+
of,
|
|
764
|
+
read,
|
|
765
|
+
rest,
|
|
766
|
+
stagger: msOf(this.args.stagger),
|
|
767
|
+
to
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* `<c.Gate />` — park the run until `c.advance()`; `@delay` opens it by
|
|
773
|
+
* itself after that many seconds (§4.1). A gate is a pause, and a pause is
|
|
774
|
+
* a total order: it may only stand in a sequence that no parallel contains.
|
|
775
|
+
*/
|
|
776
|
+
class Gate extends Component {
|
|
777
|
+
node() {
|
|
778
|
+
return {
|
|
779
|
+
kind: 'gate',
|
|
780
|
+
ms: msOf(this.args.delay)
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
mark = modifier(el => {
|
|
784
|
+
providers.set(el, this);
|
|
785
|
+
return () => providers.delete(el);
|
|
786
|
+
});
|
|
787
|
+
static {
|
|
788
|
+
setComponentTemplate(precompileTemplate("<span hidden data-choreo-step {{this.mark}}></span>", {
|
|
789
|
+
strictMode: true
|
|
790
|
+
}), this);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* A block is a step's equal to the anchor system: it takes `@name`, `@at`
|
|
795
|
+
* and `@delay` on the same terms, so a composite step — a `node()` that
|
|
796
|
+
* returns a block — is something the rest of the score can point at.
|
|
797
|
+
*/
|
|
798
|
+
class BlockComponent extends Component {
|
|
799
|
+
el;
|
|
800
|
+
node() {
|
|
801
|
+
return {
|
|
802
|
+
at: this.args.at,
|
|
803
|
+
children: this.el ? collect(this.el) : [],
|
|
804
|
+
delay: msOf(this.args.delay),
|
|
805
|
+
kind: this.kind,
|
|
806
|
+
name: this.args.name
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
mark = modifier(el => {
|
|
810
|
+
this.el = el;
|
|
811
|
+
providers.set(el, this);
|
|
812
|
+
return () => {
|
|
813
|
+
providers.delete(el);
|
|
814
|
+
this.el = undefined;
|
|
815
|
+
};
|
|
816
|
+
});
|
|
817
|
+
static {
|
|
818
|
+
setComponentTemplate(precompileTemplate("<span hidden data-choreo-block {{this.mark}}>{{yield}}</span>", {
|
|
819
|
+
strictMode: true
|
|
820
|
+
}), this);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
class Sequence extends BlockComponent {
|
|
824
|
+
kind = 'sequence';
|
|
825
|
+
}
|
|
826
|
+
class Parallel extends BlockComponent {
|
|
827
|
+
kind = 'parallel';
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
export { Aim, Attach, Camera, Camera3D, Crossing, Follow, Frame, Gate, Hold, Move, Pan, Parallel, Perform, Raise, Scroll, Sequence, SlowZoom, Spring, StepComponent, Tether, Tween, Wait, collect, toMs };
|
|
831
|
+
//# sourceMappingURL=steps.js.map
|