@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
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { modifier } from 'ember-modifier';
|
|
3
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
4
|
+
import { setComponentTemplate } from '@ember/component';
|
|
5
|
+
|
|
6
|
+
const retire = modifier((el, [seekable]) => {
|
|
7
|
+
/* a seekable seam is stood at a time by the film and taken off by it:
|
|
8
|
+
an animation that is paused never ends, and must not be lidded */
|
|
9
|
+
if (seekable) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const done = () => {
|
|
13
|
+
el.style.display = 'none';
|
|
14
|
+
};
|
|
15
|
+
el.addEventListener('animationend', done);
|
|
16
|
+
el.addEventListener('animationcancel', done);
|
|
17
|
+
/* a still that never animates at all (reduced motion, a dropped
|
|
18
|
+
stylesheet) must not become a permanent lid either */
|
|
19
|
+
const failsafe = window.setTimeout(done, 1400);
|
|
20
|
+
return () => {
|
|
21
|
+
window.clearTimeout(failsafe);
|
|
22
|
+
el.removeEventListener('animationend', done);
|
|
23
|
+
el.removeEventListener('animationcancel', done);
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
/** what a presentation is given */
|
|
27
|
+
|
|
28
|
+
/** a presentation is a Glimmer component class on the contract above — the class, not a `ComponentLike`, so an app's and the library's typings agree */
|
|
29
|
+
/** a seam the film can play: its presentation, how long it holds the picture, whether it needs a still */
|
|
30
|
+
|
|
31
|
+
/* ---- the twelve, as components -------------------------------------- */ /** the outgoing frame itself, swept off along the sun's diagonal behind a feathered edge */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
32
|
+
class Wipe extends Component {
|
|
33
|
+
static {
|
|
34
|
+
setComponentTemplate(precompileTemplate("<div class=\"cf-swipe\" aria-hidden=\"true\" {{retire @seekable}}>\n <img src={{@still}} alt />\n</div>", {
|
|
35
|
+
strictMode: true,
|
|
36
|
+
scope: () => ({
|
|
37
|
+
retire
|
|
38
|
+
})
|
|
39
|
+
}), this);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** the long soft one, no push and no colour */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
43
|
+
class Melt extends Component {
|
|
44
|
+
static {
|
|
45
|
+
setComponentTemplate(precompileTemplate("<img class=\"cf-melt\" src={{@still}} alt aria-hidden=\"true\" {{retire @seekable}} />", {
|
|
46
|
+
strictMode: true,
|
|
47
|
+
scope: () => ({
|
|
48
|
+
retire
|
|
49
|
+
})
|
|
50
|
+
}), this);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** the outgoing frame held as a still and faded over the live incoming shot */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
54
|
+
class Blend extends Component {
|
|
55
|
+
static {
|
|
56
|
+
setComponentTemplate(precompileTemplate("<img class=\"cf-blend\" src={{@still}} alt aria-hidden=\"true\" {{retire @seekable}} />", {
|
|
57
|
+
strictMode: true,
|
|
58
|
+
scope: () => ({
|
|
59
|
+
retire
|
|
60
|
+
})
|
|
61
|
+
}), this);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** the old shot closes in a circle onto the incoming subject; the centre rides inline custom properties */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
65
|
+
class Iris extends Component {
|
|
66
|
+
static {
|
|
67
|
+
setComponentTemplate(precompileTemplate("<img class=\"cf-iris\" src={{@still}} style={{@at}} alt aria-hidden=\"true\" {{retire @seekable}} />", {
|
|
68
|
+
strictMode: true,
|
|
69
|
+
scope: () => ({
|
|
70
|
+
retire
|
|
71
|
+
})
|
|
72
|
+
}), this);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** the still blurs out over the incoming shot; also the freeze half of a rack-defocus */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
76
|
+
class Blur extends Component {
|
|
77
|
+
static {
|
|
78
|
+
setComponentTemplate(precompileTemplate("<img class=\"cf-blurout\" src={{@still}} alt aria-hidden=\"true\" {{retire @seekable}} />", {
|
|
79
|
+
strictMode: true,
|
|
80
|
+
scope: () => ({
|
|
81
|
+
retire
|
|
82
|
+
})
|
|
83
|
+
}), this);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** the still dissolves by its own luminance */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
87
|
+
class Luma extends Component {
|
|
88
|
+
static {
|
|
89
|
+
setComponentTemplate(precompileTemplate("<img class=\"cf-luma\" src={{@still}} alt aria-hidden=\"true\" {{retire @seekable}} />", {
|
|
90
|
+
strictMode: true,
|
|
91
|
+
scope: () => ({
|
|
92
|
+
retire
|
|
93
|
+
})
|
|
94
|
+
}), this);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** a white frame, and the picture comes back through it */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
98
|
+
class Flash extends Component {
|
|
99
|
+
static {
|
|
100
|
+
setComponentTemplate(precompileTemplate("<i class=\"cf-flash\" aria-hidden=\"true\" {{retire @seekable}}></i>", {
|
|
101
|
+
strictMode: true,
|
|
102
|
+
scope: () => ({
|
|
103
|
+
retire
|
|
104
|
+
})
|
|
105
|
+
}), this);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** freeze under, veil over: the old shot holds while the colour closes, the seam passes in the dark, the veil lifts on the new shot */
|
|
109
|
+
class Dip extends Component {
|
|
110
|
+
get veil() {
|
|
111
|
+
return `background:${this.args.color ?? '#0d0905'}`;
|
|
112
|
+
}
|
|
113
|
+
static {
|
|
114
|
+
setComponentTemplate(precompileTemplate("<span class=\"cf-dip\" aria-hidden=\"true\" {{retire @seekable}}>\n {{#if @still}}\n <img src={{@still}} alt />\n {{/if}}\n <i style={{this.veil}}></i>\n</span>", {
|
|
115
|
+
strictMode: true,
|
|
116
|
+
scope: () => ({
|
|
117
|
+
retire
|
|
118
|
+
})
|
|
119
|
+
}), this);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A SEAM AS TWO NUMBERS, at progress `p`.
|
|
124
|
+
*
|
|
125
|
+
* `mix` is how much of the HELD frame is over the live one; `veil` is how
|
|
126
|
+
* much of a colour is over both. The picture composites them in light,
|
|
127
|
+
* and the film reveals the incoming furniture by what is left over —
|
|
128
|
+
* `1 - max(mix, veil)`. One number in, two halves out: that is the whole
|
|
129
|
+
* seam, and it is why a seek through one is free (a function of the
|
|
130
|
+
* clock, with nothing to stand at a time).
|
|
131
|
+
*
|
|
132
|
+
* Only the three the glass can hold are here. A wipe and an iris are
|
|
133
|
+
* SHAPES rather than mixes — they need pixels in the DOM to sweep and to
|
|
134
|
+
* clip — and they keep their presentation.
|
|
135
|
+
*/
|
|
136
|
+
function seamShape(kind, p) {
|
|
137
|
+
const t = Math.max(0, Math.min(1, p));
|
|
138
|
+
if (kind === 'dip') {
|
|
139
|
+
/* close, hold, open. The veil is a shutter and the picture mixes it
|
|
140
|
+
in light, so the curve is the plain smooth one: the compensation
|
|
141
|
+
the CSS veil needed was for sRGB compositing, which this is not. */
|
|
142
|
+
const veil = t < 0.34 ? smoothstep(t / 0.34) : t < 0.44 ? 1 : 1 - smoothstep((t - 0.44) / 0.56);
|
|
143
|
+
/* the held frame is swapped for the live one inside the hold, where
|
|
144
|
+
none of the change is on screen */
|
|
145
|
+
return {
|
|
146
|
+
mix: t < 0.4 ? 1 : 0,
|
|
147
|
+
veil
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/* blend and melt: ease-out, the same curve the page used to run itself */
|
|
151
|
+
const e = 1 - (1 - t) * (1 - t);
|
|
152
|
+
return {
|
|
153
|
+
mix: 1 - e,
|
|
154
|
+
veil: 0
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/** the seams the picture can hold in its own glass, given `Picture.seam` */
|
|
158
|
+
function inGlass(kind) {
|
|
159
|
+
return kind === 'blend' || kind === 'melt' || kind === 'dip';
|
|
160
|
+
}
|
|
161
|
+
function smoothstep(x) {
|
|
162
|
+
const t = Math.max(0, Math.min(1, x));
|
|
163
|
+
return t * t * (3 - 2 * t);
|
|
164
|
+
}
|
|
165
|
+
/** the film's own twelve seams, by name */
|
|
166
|
+
const PRESENTATIONS = {
|
|
167
|
+
blend: {
|
|
168
|
+
component: Blend,
|
|
169
|
+
secs: 0.52,
|
|
170
|
+
still: true
|
|
171
|
+
},
|
|
172
|
+
blur: {
|
|
173
|
+
component: Blur,
|
|
174
|
+
secs: 0.64,
|
|
175
|
+
still: true
|
|
176
|
+
},
|
|
177
|
+
cut: {
|
|
178
|
+
secs: 0,
|
|
179
|
+
still: false
|
|
180
|
+
},
|
|
181
|
+
defocus: {
|
|
182
|
+
component: Blur,
|
|
183
|
+
secs: 0.7,
|
|
184
|
+
still: true
|
|
185
|
+
},
|
|
186
|
+
dip: {
|
|
187
|
+
component: Dip,
|
|
188
|
+
secs: 0.76,
|
|
189
|
+
still: true
|
|
190
|
+
},
|
|
191
|
+
flash: {
|
|
192
|
+
component: Flash,
|
|
193
|
+
secs: 0.3,
|
|
194
|
+
still: false
|
|
195
|
+
},
|
|
196
|
+
iris: {
|
|
197
|
+
component: Iris,
|
|
198
|
+
secs: 0.9,
|
|
199
|
+
still: true
|
|
200
|
+
},
|
|
201
|
+
luma: {
|
|
202
|
+
component: Luma,
|
|
203
|
+
secs: 0.9,
|
|
204
|
+
still: true
|
|
205
|
+
},
|
|
206
|
+
melt: {
|
|
207
|
+
component: Melt,
|
|
208
|
+
secs: 1.9,
|
|
209
|
+
still: true
|
|
210
|
+
},
|
|
211
|
+
sweep: {
|
|
212
|
+
secs: 0.6,
|
|
213
|
+
still: false
|
|
214
|
+
},
|
|
215
|
+
whip: {
|
|
216
|
+
secs: 0.36,
|
|
217
|
+
still: false
|
|
218
|
+
},
|
|
219
|
+
wipe: {
|
|
220
|
+
component: Wipe,
|
|
221
|
+
secs: 1.15,
|
|
222
|
+
still: true
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
/** how long each join holds the picture, in seconds */
|
|
226
|
+
const JOIN_SECS = Object.fromEntries(Object.entries(PRESENTATIONS).map(([k, p]) => [k, p.secs]));
|
|
227
|
+
/** the joins that hold a still of the outgoing shot over the incoming one */
|
|
228
|
+
const STILL_JOINS = new Set(Object.entries(PRESENTATIONS).filter(([, p]) => p.still).map(([k]) => k));
|
|
229
|
+
/**
|
|
230
|
+
* THE SEAM ON SCREEN: whichever presentation the kind names, given the
|
|
231
|
+
* still, a new element per cut. A seam that needs a still and has none
|
|
232
|
+
* (the picture held it in its own glass) paints nothing here.
|
|
233
|
+
*/
|
|
234
|
+
class Joins extends Component {
|
|
235
|
+
get presentation() {
|
|
236
|
+
const p = this.args.kind ? this.args.presentations[this.args.kind] : null;
|
|
237
|
+
if (!p?.component) {
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
if (p.still && !this.args.freeze && this.args.kind !== 'dip') {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
return p.component;
|
|
244
|
+
}
|
|
245
|
+
static {
|
|
246
|
+
setComponentTemplate(precompileTemplate("{{#each (array @stamp) key=\"@identity\" as |s|}}\n {{#if s}}\n {{#if this.presentation}}\n <this.presentation @still={{if @freeze @freeze \"\"}} @at={{@irisAt}} @color={{@dipColor}} @seekable={{@seekable}} />\n {{/if}}\n {{/if}}\n{{/each}}", {
|
|
247
|
+
strictMode: true,
|
|
248
|
+
scope: () => ({
|
|
249
|
+
array
|
|
250
|
+
})
|
|
251
|
+
}), this);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/** `{{array x}}` — one element, so the `each` above is keyed on a scalar */
|
|
255
|
+
function array(...items) {
|
|
256
|
+
return items;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export { Blend, Blur, Dip, Flash, Iris, JOIN_SECS, Joins, Luma, Melt, PRESENTATIONS, STILL_JOINS, Wipe, inGlass, retire, seamShape };
|
|
260
|
+
//# sourceMappingURL=joins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"joins.js","sources":["../../src/film/joins.gts"],"sourcesContent":["/**\n * THE JOINS, as presentations.\n *\n * A join is what happens to the OUTGOING frame while the incoming shot\n * plays underneath it from its very first frame: the still is swept off,\n * thinned, closed in a circle, covered with a colour. That is one\n * contract — a component given the still and told how long it has — and\n * the film's twelve seams are twelve components that satisfy it. A\n * thirteenth, written in an app, satisfies it the same way and is named\n * from a shot's `<f.Join @presentation={{MyWipe}} />`; the film learns\n * nothing about it but its length.\n *\n * Three of the twelve are not presentations at all: a `cut` and a\n * `whip` are the camera's, a `sweep` is the light's, and the film plays\n * those itself. Three more — `blend`, `melt`, `dip` — the picture can\n * do in its own glass (`Picture.dissolve`) and only fall back to a still\n * here when it cannot.\n *\n * A seam that never ends is a lid: every overlay retires itself when its\n * animation ends, and a seekable one (stood at a time by an exact film)\n * is taken off by the film instead.\n */\nimport Component from '@glimmer/component';\nimport { modifier } from 'ember-modifier';\n\nimport type { Join, JoinName, Over } from './types.ts';\n\nexport const retire = modifier(\n (el: HTMLElement, [seekable]: [boolean | undefined]) => {\n /* a seekable seam is stood at a time by the film and taken off by it:\n an animation that is paused never ends, and must not be lidded */\n if (seekable) {\n return;\n }\n const done = () => {\n el.style.display = 'none';\n };\n el.addEventListener('animationend', done);\n el.addEventListener('animationcancel', done);\n /* a still that never animates at all (reduced motion, a dropped\n stylesheet) must not become a permanent lid either */\n const failsafe = window.setTimeout(done, 1400);\n return () => {\n window.clearTimeout(failsafe);\n el.removeEventListener('animationend', done);\n el.removeEventListener('animationcancel', done);\n };\n },\n);\n\n/** what a presentation is given */\nexport interface PresentationSignature {\n Args: {\n /** where the seam is aimed, as inline custom properties (an iris's centre) */\n at?: string;\n /** the colour a dip passes through */\n color?: string;\n /** the film stands the overlay at a time itself: never retire on `animationend` */\n seekable?: boolean;\n /** the outgoing frame, as a data URL; empty when the picture held it in its own glass */\n still: string;\n };\n}\n\n/** a presentation is a Glimmer component class on the contract above — the class, not a `ComponentLike`, so an app's and the library's typings agree */\nexport type PresentationComponent = typeof Component<PresentationSignature>;\n\n/** a seam the film can play: its presentation, how long it holds the picture, whether it needs a still */\nexport interface Presentation {\n /** the overlay; none for a seam the camera or the light plays */\n component?: PresentationComponent;\n /**\n * How deep this seam goes by default, when the score does not say\n * (`Over`). A presentation an app brings may want `everything` — a\n * curtain that drops in front of the picture and leaves the caption\n * standing in front of it is not a curtain.\n */\n over?: Over;\n /** seconds the seam holds the picture */\n secs: number;\n /** the seam paints a still of the outgoing frame over the incoming one */\n still: boolean;\n}\n\n/* ---- the twelve, as components -------------------------------------- */\n\n/** the outgoing frame itself, swept off along the sun's diagonal behind a feathered edge */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Wipe extends Component<PresentationSignature> {\n <template>\n <div class='cf-swipe' aria-hidden='true' {{retire @seekable}}>\n <img src={{@still}} alt='' />\n </div>\n </template>\n}\n\n/** the long soft one, no push and no colour */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Melt extends Component<PresentationSignature> {\n <template>\n <img\n class='cf-melt'\n src={{@still}}\n alt=''\n aria-hidden='true'\n {{retire @seekable}}\n />\n </template>\n}\n\n/** the outgoing frame held as a still and faded over the live incoming shot */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Blend extends Component<PresentationSignature> {\n <template>\n <img\n class='cf-blend'\n src={{@still}}\n alt=''\n aria-hidden='true'\n {{retire @seekable}}\n />\n </template>\n}\n\n/** the old shot closes in a circle onto the incoming subject; the centre rides inline custom properties */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Iris extends Component<PresentationSignature> {\n <template>\n <img\n class='cf-iris'\n src={{@still}}\n style={{@at}}\n alt=''\n aria-hidden='true'\n {{retire @seekable}}\n />\n </template>\n}\n\n/** the still blurs out over the incoming shot; also the freeze half of a rack-defocus */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Blur extends Component<PresentationSignature> {\n <template>\n <img\n class='cf-blurout'\n src={{@still}}\n alt=''\n aria-hidden='true'\n {{retire @seekable}}\n />\n </template>\n}\n\n/** the still dissolves by its own luminance */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Luma extends Component<PresentationSignature> {\n <template>\n <img\n class='cf-luma'\n src={{@still}}\n alt=''\n aria-hidden='true'\n {{retire @seekable}}\n />\n </template>\n}\n\n/** a white frame, and the picture comes back through it */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Flash extends Component<PresentationSignature> {\n <template>\n <i class='cf-flash' aria-hidden='true' {{retire @seekable}}></i>\n </template>\n}\n\n/** freeze under, veil over: the old shot holds while the colour closes, the seam passes in the dark, the veil lifts on the new shot */\nexport class Dip extends Component<PresentationSignature> {\n get veil(): string {\n return `background:${this.args.color ?? '#0d0905'}`;\n }\n\n <template>\n <span class='cf-dip' aria-hidden='true' {{retire @seekable}}>\n {{#if @still}}\n <img src={{@still}} alt='' />\n {{/if}}\n <i style={{this.veil}}></i>\n </span>\n </template>\n}\n\n/**\n * A SEAM AS TWO NUMBERS, at progress `p`.\n *\n * `mix` is how much of the HELD frame is over the live one; `veil` is how\n * much of a colour is over both. The picture composites them in light,\n * and the film reveals the incoming furniture by what is left over —\n * `1 - max(mix, veil)`. One number in, two halves out: that is the whole\n * seam, and it is why a seek through one is free (a function of the\n * clock, with nothing to stand at a time).\n *\n * Only the three the glass can hold are here. A wipe and an iris are\n * SHAPES rather than mixes — they need pixels in the DOM to sweep and to\n * clip — and they keep their presentation.\n */\nexport function seamShape(\n kind: JoinName,\n p: number,\n): { mix: number; veil: number } {\n const t = Math.max(0, Math.min(1, p));\n if (kind === 'dip') {\n /* close, hold, open. The veil is a shutter and the picture mixes it\n in light, so the curve is the plain smooth one: the compensation\n the CSS veil needed was for sRGB compositing, which this is not. */\n const veil =\n t < 0.34\n ? smoothstep(t / 0.34)\n : t < 0.44\n ? 1\n : 1 - smoothstep((t - 0.44) / 0.56);\n /* the held frame is swapped for the live one inside the hold, where\n none of the change is on screen */\n return { mix: t < 0.4 ? 1 : 0, veil };\n }\n /* blend and melt: ease-out, the same curve the page used to run itself */\n const e = 1 - (1 - t) * (1 - t);\n return { mix: 1 - e, veil: 0 };\n}\n\n/** the seams the picture can hold in its own glass, given `Picture.seam` */\nexport function inGlass(kind: JoinName): boolean {\n return kind === 'blend' || kind === 'melt' || kind === 'dip';\n}\n\nfunction smoothstep(x: number): number {\n const t = Math.max(0, Math.min(1, x));\n return t * t * (3 - 2 * t);\n}\n\n/** the film's own twelve seams, by name */\nexport const PRESENTATIONS: Record<Join, Presentation> = {\n blend: { component: Blend, secs: 0.52, still: true },\n blur: { component: Blur, secs: 0.64, still: true },\n cut: { secs: 0, still: false },\n defocus: { component: Blur, secs: 0.7, still: true },\n dip: { component: Dip, secs: 0.76, still: true },\n flash: { component: Flash, secs: 0.3, still: false },\n iris: { component: Iris, secs: 0.9, still: true },\n luma: { component: Luma, secs: 0.9, still: true },\n melt: { component: Melt, secs: 1.9, still: true },\n sweep: { secs: 0.6, still: false },\n whip: { secs: 0.36, still: false },\n wipe: { component: Wipe, secs: 1.15, still: true },\n};\n\n/** how long each join holds the picture, in seconds */\nexport const JOIN_SECS: Record<string, number> = Object.fromEntries(\n Object.entries(PRESENTATIONS).map(([k, p]) => [k, p.secs]),\n);\n\n/** the joins that hold a still of the outgoing shot over the incoming one */\nexport const STILL_JOINS: ReadonlySet<string> = new Set(\n Object.entries(PRESENTATIONS)\n .filter(([, p]) => p.still)\n .map(([k]) => k),\n);\n\nexport interface JoinsSignature {\n Args: {\n /** the colour a dip passes through */\n dipColor?: string;\n /** the outgoing frame, when the page could not hold it in its own glass */\n freeze?: string;\n /** the iris's centre, as inline custom properties */\n irisAt?: string;\n /** which seam is playing */\n kind: '' | JoinName;\n /** every seam the film knows: the twelve, and any a score brought */\n presentations: Record<string, Presentation>;\n /** the film drives the overlay's clock: it is never retired by its own end */\n seekable?: boolean;\n /**\n * A NEW ELEMENT PER CUT. Keyed on the stamp so each overlay plays from\n * its own first frame; the number only ever goes up.\n */\n stamp: number;\n };\n}\n\n/**\n * THE SEAM ON SCREEN: whichever presentation the kind names, given the\n * still, a new element per cut. A seam that needs a still and has none\n * (the picture held it in its own glass) paints nothing here.\n */\nexport class Joins extends Component<JoinsSignature> {\n get presentation(): PresentationComponent | undefined {\n const p = this.args.kind ? this.args.presentations[this.args.kind] : null;\n if (!p?.component) {\n return undefined;\n }\n if (p.still && !this.args.freeze && this.args.kind !== 'dip') {\n return undefined;\n }\n return p.component;\n }\n\n <template>\n {{#each (array @stamp) key='@identity' as |s|}}\n {{#if s}}\n {{#if this.presentation}}\n <this.presentation\n @still={{if @freeze @freeze ''}}\n @at={{@irisAt}}\n @color={{@dipColor}}\n @seekable={{@seekable}}\n />\n {{/if}}\n {{/if}}\n {{/each}}\n </template>\n}\n\n/** `{{array x}}` — one element, so the `each` above is keyed on a scalar */\nfunction array<T>(...items: T[]): T[] {\n return items;\n}\n"],"names":["retire","modifier","el","seekable","done","style","display","addEventListener","failsafe","window","setTimeout","clearTimeout","removeEventListener","Wipe","Component","setComponentTemplate","precompileTemplate","strictMode","scope","Melt","Blend","Iris","Blur","Luma","Flash","Dip","veil","args","color","seamShape","kind","p","t","Math","max","min","smoothstep","mix","e","inGlass","x","PRESENTATIONS","blend","component","secs","still","blur","cut","defocus","dip","flash","iris","luma","melt","sweep","whip","wipe","JOIN_SECS","Object","fromEntries","entries","map","k","STILL_JOINS","Set","filter","Joins","presentation","presentations","undefined","freeze","array","items"],"mappings":";;;;;AA2BO,MAAMA,MAAA,GAASC,QAAA,CACpB,CAACC,EAAI,EAAa,CAACC,QAAA,CAAgC,KAAA;AACjD;AACkE;AAClE,EAAA,IAAIA,QAAA,EAAU;AACZ,IAAA;AACF,EAAA;EACA,MAAMC,IAAA,GAAOA,MAAA;AACXF,IAAAA,EAAA,CAAGG,KAAK,CAACC,OAAO,GAAG,MAAA;EACrB,CAAA;AACAJ,EAAAA,EAAA,CAAGK,gBAAgB,CAAC,cAAA,EAAgBH,IAAA,CAAA;AACpCF,EAAAA,EAAA,CAAGK,gBAAgB,CAAC,iBAAA,EAAmBH,IAAA,CAAA;AACvC;AACsD;EACtD,MAAMI,QAAA,GAAWC,MAAA,CAAOC,UAAU,CAACN,IAAA,EAAM,IAAA,CAAA;AACzC,EAAA,OAAO,MAAA;AACLK,IAAAA,MAAA,CAAOE,YAAY,CAACH,QAAA,CAAA;AACpBN,IAAAA,EAAA,CAAGU,mBAAmB,CAAC,cAAA,EAAgBR,IAAA,CAAA;AACvCF,IAAAA,EAAA,CAAGU,mBAAmB,CAAC,iBAAA,EAAmBR,IAAA,CAAA;EAC5C,CAAA;AACF,CAAA;AAGF;;AAcA;AAGA;;AAiBA;AAIO,MAAMS,aAAaC,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,0GAAA,EAIA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAlB,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;AAEO,MAAMmB,aAAaL,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,wFAAA,EAQA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAlB,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;AAEO,MAAMoB,cAAcN,SAAA,CAAU;AACnC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,yFAAA,EAQA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAlB,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;AAEO,MAAMqB,aAAaP,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,sGAAA,EASA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAlB,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;AAEO,MAAMsB,aAAaR,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,2FAAA,EAQA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAlB,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;AAEO,MAAMuB,aAAaT,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,wFAAA,EAQA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAlB,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;AAEO,MAAMwB,cAAcV,SAAA,CAAU;AACnC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,sEAAA,EAEA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAlB,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;AACO,MAAMyB,GAAA,SAAYX,SAAA,CAAU;EACjC,IAAIY,IAAAA,GAAe;IACjB,OAAO,CAAA,WAAA,EAAc,IAAI,CAACC,IAAI,CAACC,KAAK,IAAI,SAAA,CAAA,CAAW;AACrD,EAAA;AAEA,EAAA;IAAAb,oBAAA,CAAAC,kBAAA,CAAA,wKAAA,EAOA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAlB,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;;;;;;;;;;;;;AAaC;AACM,SAAS6B,SAAAA,CACdC,IAAc,EACdC,CAAS;AAET,EAAA,MAAMC,CAAA,GAAIC,KAAKC,GAAG,CAAC,GAAGD,IAAA,CAAKE,GAAG,CAAC,CAAA,EAAGJ,CAAA,CAAA,CAAA;EAClC,IAAID,SAAS,KAAA,EAAO;AAClB;;AAEoE;AACpE,IAAA,MAAMJ,IAAA,GACJM,CAAA,GAAI,IAAA,GACAI,WAAWJ,CAAA,GAAI,IAAA,CAAA,GACfA,CAAA,GAAI,IAAA,GACF,IACA,CAAA,GAAII,UAAA,CAAW,CAACJ,CAAA,GAAI,IAAI,IAAI,IAAA,CAAA;AACpC;AACmC;IACnC,OAAO;AAAEK,MAAAA,GAAA,EAAKL,CAAA,GAAI,MAAM,CAAA,GAAI,CAAA;AAAGN,MAAAA;KAAK;AACtC,EAAA;AACA;AACA,EAAA,MAAMY,CAAA,GAAI,CAAA,GAAI,CAAC,IAAIN,CAAC,KAAK,CAAA,GAAIA,CAAC,CAAA;EAC9B,OAAO;IAAEK,GAAA,EAAK,CAAA,GAAIC,CAAA;AAAGZ,IAAAA,IAAA,EAAM;GAAE;AAC/B;AAEA;AACO,SAASa,QAAQT,IAAc,EAAU;EAC9C,OAAOA,IAAA,KAAS,OAAA,IAAWA,IAAA,KAAS,MAAA,IAAUA,IAAA,KAAS,KAAA;AACzD;AAEA,SAASM,UAAAA,CAAWI,CAAS,EAAS;AACpC,EAAA,MAAMR,CAAA,GAAIC,KAAKC,GAAG,CAAC,GAAGD,IAAA,CAAKE,GAAG,CAAC,CAAA,EAAGK,CAAA,CAAA,CAAA;EAClC,OAAOR,CAAA,GAAIA,CAAA,IAAK,CAAA,GAAI,IAAIA,CAAC,CAAA;AAC3B;AAEA;AACO,MAAMS,aAA4B,GAAgB;AACvDC,EAAAA,KAAA,EAAO;AAAEC,IAAAA,SAAA,EAAWvB,KAAA;AAAOwB,IAAAA,IAAA,EAAM,IAAA;AAAMC,IAAAA,KAAA,EAAO;GAAK;AACnDC,EAAAA,IAAA,EAAM;AAAEH,IAAAA,SAAA,EAAWrB,IAAA;AAAMsB,IAAAA,IAAA,EAAM,IAAA;AAAMC,IAAAA,KAAA,EAAO;GAAK;AACjDE,EAAAA,GAAA,EAAK;AAAEH,IAAAA,IAAA,EAAM,CAAA;AAAGC,IAAAA,KAAA,EAAO;GAAM;AAC7BG,EAAAA,OAAA,EAAS;AAAEL,IAAAA,SAAA,EAAWrB,IAAA;AAAMsB,IAAAA,IAAA,EAAM,GAAA;AAAKC,IAAAA,KAAA,EAAO;GAAK;AACnDI,EAAAA,GAAA,EAAK;AAAEN,IAAAA,SAAA,EAAWlB,GAAA;AAAKmB,IAAAA,IAAA,EAAM,IAAA;AAAMC,IAAAA,KAAA,EAAO;GAAK;AAC/CK,EAAAA,KAAA,EAAO;AAAEP,IAAAA,SAAA,EAAWnB,KAAA;AAAOoB,IAAAA,IAAA,EAAM,GAAA;AAAKC,IAAAA,KAAA,EAAO;GAAM;AACnDM,EAAAA,IAAA,EAAM;AAAER,IAAAA,SAAA,EAAWtB,IAAA;AAAMuB,IAAAA,IAAA,EAAM,GAAA;AAAKC,IAAAA,KAAA,EAAO;GAAK;AAChDO,EAAAA,IAAA,EAAM;AAAET,IAAAA,SAAA,EAAWpB,IAAA;AAAMqB,IAAAA,IAAA,EAAM,GAAA;AAAKC,IAAAA,KAAA,EAAO;GAAK;AAChDQ,EAAAA,IAAA,EAAM;AAAEV,IAAAA,SAAA,EAAWxB,IAAA;AAAMyB,IAAAA,IAAA,EAAM,GAAA;AAAKC,IAAAA,KAAA,EAAO;GAAK;AAChDS,EAAAA,KAAA,EAAO;AAAEV,IAAAA,IAAA,EAAM,GAAA;AAAKC,IAAAA,KAAA,EAAO;GAAM;AACjCU,EAAAA,IAAA,EAAM;AAAEX,IAAAA,IAAA,EAAM,IAAA;AAAMC,IAAAA,KAAA,EAAO;GAAM;AACjCW,EAAAA,IAAA,EAAM;AAAEb,IAAAA,SAAA,EAAW9B,IAAA;AAAM+B,IAAAA,IAAA,EAAM,IAAA;AAAMC,IAAAA,KAAA,EAAO;AAAK;AACnD;AAEA;AACO,MAAMY,SAAgC,GAAIC,MAAA,CAAOC,WAAW,CACjED,MAAA,CAAOE,OAAO,CAACnB,aAAA,CAAA,CAAeoB,GAAG,CAAC,CAAC,CAACC,CAAA,EAAG/B,CAAA,CAAE,KAAK,CAAC+B,CAAA,EAAG/B,CAAA,CAAEa,IAAI,CAAC,CAAA;AAG3D;AACO,MAAMmB,WAA+B,GAAI,IAAIC,GAAA,CAClDN,MAAA,CAAOE,OAAO,CAACnB,aAAA,CAAA,CACZwB,MAAM,CAAC,CAAC,GAAGlC,CAAA,CAAE,KAAKA,CAAA,CAAEc,KAAK,CAAA,CACzBgB,GAAG,CAAC,CAAC,CAACC,CAAA,CAAE,KAAKA,CAAA,CAAA;AAyBlB;;;;;AAKO,MAAMI,KAAA,SAAcpD,SAAA,CAAU;EACnC,IAAIqD,YAAAA,GAAkD;IACpD,MAAMpC,IAAI,IAAI,CAACJ,IAAI,CAACG,IAAI,GAAG,IAAI,CAACH,IAAI,CAACyC,aAAa,CAAC,IAAI,CAACzC,IAAI,CAACG,IAAI,CAAC,GAAG,IAAA;AACrE,IAAA,IAAI,CAACC,GAAGY,SAAA,EAAW;AACjB,MAAA,OAAO0B,SAAA;AACT,IAAA;AACA,IAAA,IAAItC,EAAEc,KAAK,IAAI,CAAC,IAAI,CAAClB,IAAI,CAAC2C,MAAM,IAAI,IAAI,CAAC3C,IAAI,CAACG,IAAI,KAAK,KAAA,EAAO;AAC5D,MAAA,OAAOuC,SAAA;AACT,IAAA;IACA,OAAOtC,EAAEY,SAAS;AACpB,EAAA;AAEA,EAAA;IAAA5B,oBAAA,CAAAC,kBAAA,CAAA,+PAAA,EAaA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAqD,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;AACA,SAASA,KAAAA,CAAS,GAAGC,KAAU,EAAG;AAChC,EAAA,OAAOA,KAAA;AACT;;;;"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** the small arithmetic every film does: the same four lines, once */
|
|
2
|
+
|
|
3
|
+
const RAD = Math.PI / 180;
|
|
4
|
+
const lerp = (a, b, t) => a + (b - a) * t;
|
|
5
|
+
const clamp01 = v => Math.max(0, Math.min(1, v));
|
|
6
|
+
|
|
7
|
+
/** smoothstep on [0,1] */
|
|
8
|
+
const smooth = t => {
|
|
9
|
+
const k = clamp01(t);
|
|
10
|
+
return k * k * (3 - 2 * k);
|
|
11
|
+
};
|
|
12
|
+
const hex = h => [parseInt(h.slice(1, 3), 16) / 255, parseInt(h.slice(3, 5), 16) / 255, parseInt(h.slice(5, 7), 16) / 255];
|
|
13
|
+
|
|
14
|
+
/** relative luminance of a #rrggbb, 0..1 */
|
|
15
|
+
const luminance = h => {
|
|
16
|
+
if (!/^#[0-9a-f]{6}$/i.test(h)) {
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
const [r, g, b] = hex(h);
|
|
20
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** a #rrggbb at an alpha, as CSS */
|
|
24
|
+
const rgba = (h, a) => {
|
|
25
|
+
if (!/^#[0-9a-f]{6}$/i.test(h)) {
|
|
26
|
+
return h;
|
|
27
|
+
}
|
|
28
|
+
const [r, g, b] = hex(h);
|
|
29
|
+
return `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${a})`;
|
|
30
|
+
};
|
|
31
|
+
const mmss = s => {
|
|
32
|
+
const m = Math.floor(Math.max(0, s) / 60);
|
|
33
|
+
return `${m}:${String(Math.floor(Math.max(0, s) % 60)).padStart(2, '0')}`;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** the shortest way round a circle, radians */
|
|
37
|
+
const wrapAngle = d => {
|
|
38
|
+
let k = d;
|
|
39
|
+
while (k > Math.PI) {
|
|
40
|
+
k -= Math.PI * 2;
|
|
41
|
+
}
|
|
42
|
+
while (k < -Math.PI) {
|
|
43
|
+
k += Math.PI * 2;
|
|
44
|
+
}
|
|
45
|
+
return k;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export { RAD, clamp01, hex, lerp, luminance, mmss, rgba, smooth, wrapAngle };
|
|
49
|
+
//# sourceMappingURL=math.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.js","sources":["../../src/film/math.ts"],"sourcesContent":["/** the small arithmetic every film does: the same four lines, once */\n\nexport const RAD = Math.PI / 180;\n\nexport const lerp = (a: number, b: number, t: number): number =>\n a + (b - a) * t;\n\nexport const clamp01 = (v: number): number => Math.max(0, Math.min(1, v));\n\n/** smoothstep on [0,1] */\nexport const smooth = (t: number): number => {\n const k = clamp01(t);\n return k * k * (3 - 2 * k);\n};\n\nexport const hex = (h: string): [number, number, number] => [\n parseInt(h.slice(1, 3), 16) / 255,\n parseInt(h.slice(3, 5), 16) / 255,\n parseInt(h.slice(5, 7), 16) / 255,\n];\n\n/** relative luminance of a #rrggbb, 0..1 */\nexport const luminance = (h: string): number => {\n if (!/^#[0-9a-f]{6}$/i.test(h)) {\n return 1;\n }\n const [r, g, b] = hex(h);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n};\n\n/** a #rrggbb at an alpha, as CSS */\nexport const rgba = (h: string, a: number): string => {\n if (!/^#[0-9a-f]{6}$/i.test(h)) {\n return h;\n }\n const [r, g, b] = hex(h);\n return `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${a})`;\n};\n\nexport const mmss = (s: number): string => {\n const m = Math.floor(Math.max(0, s) / 60);\n return `${m}:${String(Math.floor(Math.max(0, s) % 60)).padStart(2, '0')}`;\n};\n\n/** the shortest way round a circle, radians */\nexport const wrapAngle = (d: number): number => {\n let k = d;\n while (k > Math.PI) {\n k -= Math.PI * 2;\n }\n while (k < -Math.PI) {\n k += Math.PI * 2;\n }\n return k;\n};\n"],"names":["RAD","Math","PI","lerp","a","b","t","clamp01","v","max","min","smooth","k","hex","h","parseInt","slice","luminance","test","r","g","rgba","round","mmss","s","m","floor","String","padStart","wrapAngle","d"],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { on } from '@ember/modifier';
|
|
2
|
+
import Component from '@glimmer/component';
|
|
3
|
+
import { motion } from 'glimmer-motion';
|
|
4
|
+
import { Choreo } from '../choreo.js';
|
|
5
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
6
|
+
import { setComponentTemplate } from '@ember/component';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* THE PHOTOGRAPH, cut in beside the model. The model is a model, and
|
|
10
|
+
* there is a limit to what a film can claim with one; a real building at
|
|
11
|
+
* the moment the commentary names it settles the claim. Wiped rather
|
|
12
|
+
* than faded — a fade says "meanwhile", a wipe says "and here it is" —
|
|
13
|
+
* and it carries its own credit.
|
|
14
|
+
*/ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
15
|
+
class Insert extends Component {
|
|
16
|
+
static {
|
|
17
|
+
setComponentTemplate(precompileTemplate("<Choreo @id=\"insert\" class=\"cf-photo\" as |g|>\n {{#each (array @beat) key=\"id\" as |b|}}\n <figure class=\"is-{{b.mode}}\" {{motion id=\"photo\" role=\"shot\"}}>\n <img src={{@src}} alt {{on \"error\" @missing}} />\n <figcaption>\n <span class=\"cf-photo-cap\">{{b.photo.caption}}</span>\n <span class=\"cf-photo-cr\">{{b.photo.credit}}</span>\n </figcaption>\n </figure>\n {{/each}}\n <g.Tween @of={{g.inserted \"shot\"}} @clipPath={{array \"inset(0 100% 0 0)\" \"inset(0 0% 0 0)\"}} @duration={{0.62}} @ease={{array 0.22 1 0.36 1}} />\n <g.Tween @of={{g.removed \"shot\"}} @opacity={{0}} @duration={{0.24}} @ease=\"easeIn\" />\n</Choreo>", {
|
|
18
|
+
strictMode: true,
|
|
19
|
+
scope: () => ({
|
|
20
|
+
Choreo,
|
|
21
|
+
array,
|
|
22
|
+
motion,
|
|
23
|
+
on
|
|
24
|
+
})
|
|
25
|
+
}), this);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** CAPTIONS: the narration, printed — an accessibility track, and before
|
|
29
|
+
* any voice exists the only way to find out a line is too long */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
30
|
+
class Captions extends Component {
|
|
31
|
+
static {
|
|
32
|
+
setComponentTemplate(precompileTemplate("{{#if @line}}\n <p class=\"cf-subs\">{{@line}}</p>\n{{/if}}", {
|
|
33
|
+
strictMode: true
|
|
34
|
+
}), this);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* THE GLASS keeps only what must connect DOM to world — the leader from a
|
|
39
|
+
* caption, the mark's tether, the ring on a named point — projected
|
|
40
|
+
* every frame by the film. The traces themselves live IN the scene.
|
|
41
|
+
*/ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
42
|
+
class Track extends Component {
|
|
43
|
+
static {
|
|
44
|
+
setComponentTemplate(precompileTemplate("<svg class=\"cf-track\" aria-hidden=\"true\" {{@wire}}>\n <line class=\"cf-leader\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"0\" />\n <line class=\"cf-tether\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"0\" />\n <circle cx=\"0\" cy=\"0\" r=\"7\" pathLength=\"1\" />\n</svg>", {
|
|
45
|
+
strictMode: true
|
|
46
|
+
}), this);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** THE STAMP: a hanko, not a title — it lands hard, settles at once, and
|
|
50
|
+
* is simply replaced by the next; the lineup's roll call */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
51
|
+
class Stamp extends Component {
|
|
52
|
+
static {
|
|
53
|
+
setComponentTemplate(precompileTemplate("{{#if @word}}\n {{#each (array @step) key=\"@identity\"}}\n <p class=\"cf-stamp\" aria-hidden=\"true\">{{@word}}</p>\n {{/each}}\n{{/if}}", {
|
|
54
|
+
strictMode: true,
|
|
55
|
+
scope: () => ({
|
|
56
|
+
array
|
|
57
|
+
})
|
|
58
|
+
}), this);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function array(...items) {
|
|
62
|
+
return items;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { Captions, Insert, Stamp, Track };
|
|
66
|
+
//# sourceMappingURL=overlays.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlays.js","sources":["../../src/film/overlays.gts"],"sourcesContent":["import { on } from '@ember/modifier';\nimport Component from '@glimmer/component';\nimport { motion } from 'glimmer-motion';\n\nimport { Choreo } from '../choreo.gts';\nimport type { Beat, ElementModifier } from './types.ts';\n\nexport interface InsertSignature {\n Args: {\n beat: Beat;\n /** the photograph failed to load: drop the plate */\n missing: () => void;\n src: string;\n };\n}\n\n/**\n * THE PHOTOGRAPH, cut in beside the model. The model is a model, and\n * there is a limit to what a film can claim with one; a real building at\n * the moment the commentary names it settles the claim. Wiped rather\n * than faded — a fade says \"meanwhile\", a wipe says \"and here it is\" —\n * and it carries its own credit.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Insert extends Component<InsertSignature> {\n <template>\n <Choreo @id='insert' class='cf-photo' as |g|>\n {{#each (array @beat) key='id' as |b|}}\n <figure class='is-{{b.mode}}' {{motion id='photo' role='shot'}}>\n <img src={{@src}} alt='' {{on 'error' @missing}} />\n <figcaption>\n <span class='cf-photo-cap'>{{b.photo.caption}}</span>\n <span class='cf-photo-cr'>{{b.photo.credit}}</span>\n </figcaption>\n </figure>\n {{/each}}\n <g.Tween\n @of={{g.inserted 'shot'}}\n @clipPath={{array 'inset(0 100% 0 0)' 'inset(0 0% 0 0)'}}\n @duration={{0.62}}\n @ease={{array 0.22 1 0.36 1}}\n />\n <g.Tween\n @of={{g.removed 'shot'}}\n @opacity={{0}}\n @duration={{0.24}}\n @ease='easeIn'\n />\n </Choreo>\n </template>\n}\n\nexport interface CaptionsSignature {\n Args: {\n /** the line being spoken */\n line?: string;\n };\n}\n\n/** CAPTIONS: the narration, printed — an accessibility track, and before\n * any voice exists the only way to find out a line is too long */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Captions extends Component<CaptionsSignature> {\n <template>\n {{#if @line}}\n <p class='cf-subs'>{{@line}}</p>\n {{/if}}\n </template>\n}\n\nexport interface TrackSignature {\n Args: {\n /** hands the film its leader, tether and ring */\n wire: ElementModifier<SVGSVGElement>;\n };\n}\n\n/**\n * THE GLASS keeps only what must connect DOM to world — the leader from a\n * caption, the mark's tether, the ring on a named point — projected\n * every frame by the film. The traces themselves live IN the scene.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Track extends Component<TrackSignature> {\n <template>\n <svg class='cf-track' aria-hidden='true' {{@wire}}>\n <line class='cf-leader' x1='0' y1='0' x2='0' y2='0' />\n <line class='cf-tether' x1='0' y1='0' x2='0' y2='0' />\n <circle cx='0' cy='0' r='7' pathLength='1' />\n </svg>\n </template>\n}\n\nexport interface StampSignature {\n Args: {\n /** a new element per step, so the strike replays from its own first frame */\n step: number;\n word: string;\n };\n}\n\n/** THE STAMP: a hanko, not a title — it lands hard, settles at once, and\n * is simply replaced by the next; the lineup's roll call */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Stamp extends Component<StampSignature> {\n <template>\n {{#if @word}}\n {{#each (array @step) key='@identity'}}\n <p class='cf-stamp' aria-hidden='true'>{{@word}}</p>\n {{/each}}\n {{/if}}\n </template>\n}\n\nfunction array<T>(...items: T[]): T[] {\n return items;\n}\n"],"names":["Insert","Component","setComponentTemplate","precompileTemplate","strictMode","scope","Choreo","array","motion","on","Captions","Track","Stamp","items"],"mappings":";;;;;;;AAgBA;;;;;;AAMC;AAEM,MAAMA,eAAeC,SAAA,CAAU;AACpC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,6qBAAA,EAwBA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,MAAA;QAAAC,KAAA;QAAAC,MAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AASA;AACiE;AAE1D,MAAMC,iBAAiBT,SAAA,CAAU;AACtC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,8DAAA,EAIA;MAAAC,UAAA,EAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AASA;;;;AAIC;AAEM,MAAMO,cAAcV,SAAA,CAAU;AACnC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,kQAAA,EAMA;MAAAC,UAAA,EAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAUA;AAC2D;AAEpD,MAAMQ,cAAcX,SAAA,CAAU;AACnC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,gJAAA,EAMA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAE,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASA,KAAAA,CAAS,GAAGM,KAAU,EAAG;AAChC,EAAA,OAAOA,KAAA;AACT;;;;"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { modifier } from 'ember-modifier';
|
|
3
|
+
import { IFRAME_PICTURE } from './graph/adjust.js';
|
|
4
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
5
|
+
import { setComponentTemplate } from '@ember/component';
|
|
6
|
+
|
|
7
|
+
/** a WebGL page in an iframe, addressed through the `Picture` port: both reference films' kind */class IframePicture extends Component {
|
|
8
|
+
pending = 0;
|
|
9
|
+
get spec() {
|
|
10
|
+
const a = this.args;
|
|
11
|
+
return {
|
|
12
|
+
accent: a.accent,
|
|
13
|
+
adjustments: IFRAME_PICTURE,
|
|
14
|
+
assets: a.assets,
|
|
15
|
+
cityGlass: a.cityGlass,
|
|
16
|
+
cloudHaze: a.cloudHaze,
|
|
17
|
+
gradeLum: a.gradeLum,
|
|
18
|
+
grades: a.grades,
|
|
19
|
+
lookFx: a.lookFx,
|
|
20
|
+
lutAmount: a.lutAmount,
|
|
21
|
+
poster: a.poster,
|
|
22
|
+
rigMid: a.rigMid,
|
|
23
|
+
seams: a.seams,
|
|
24
|
+
seat: a.seat,
|
|
25
|
+
src: a.src,
|
|
26
|
+
srcdoc: a.srcdoc,
|
|
27
|
+
standing: a.standing,
|
|
28
|
+
title: a.title,
|
|
29
|
+
worldType: a.worldType
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** registers a frame after render, and again whenever an argument changes */
|
|
33
|
+
attach = modifier(() => {
|
|
34
|
+
const spec = this.spec; // read here, so the modifier tracks every argument
|
|
35
|
+
if (this.pending) {
|
|
36
|
+
cancelAnimationFrame(this.pending);
|
|
37
|
+
}
|
|
38
|
+
this.pending = requestAnimationFrame(() => {
|
|
39
|
+
this.pending = 0;
|
|
40
|
+
this.args.register(spec);
|
|
41
|
+
});
|
|
42
|
+
return () => {
|
|
43
|
+
if (this.pending) {
|
|
44
|
+
cancelAnimationFrame(this.pending);
|
|
45
|
+
this.pending = 0;
|
|
46
|
+
}
|
|
47
|
+
this.args.register(null);
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
static {
|
|
51
|
+
setComponentTemplate(precompileTemplate("<span hidden data-film-picture {{this.attach}}></span>", {
|
|
52
|
+
strictMode: true
|
|
53
|
+
}), this);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { IframePicture };
|
|
58
|
+
//# sourceMappingURL=picture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"picture.js","sources":["../../src/film/picture.gts"],"sourcesContent":["/**\n * THE PICTURE, AS A COMPONENT.\n *\n * A film does not own a picture; it is handed one. Until Phase 2 it was\n * handed fourteen arguments about the page behind the iframe — where it\n * lives, where its files are, how it is seated before the door, the rig's\n * mid-height, how much frosted city a frame can carry — and none of them\n * were about the edit. Now the picture is a component in the film's\n * `<:picture>` block, and those are ITS arguments:\n *\n * <Film @name='sagrada' @seek='exact' @clock={{CLOCK}}>\n * <:picture>\n * <IframePicture @src={{this.src}} @assets={{this.assets}}\n * @standing={{T_TODAY}} @seat={{seat}} @rigMid={{6.6}} @cityGlass={{0.16}}\n * @grades={{GRADES}} @lookFx={{LOOK_FX}} @title='Sagrada Família' />\n * </:picture>\n * <:default as |f|>…the score…</:default>\n * </Film>\n *\n * The component renders nothing; it REGISTERS its spec with the film (the\n * block yields the registrar), a frame after render so the write never\n * lands inside the render that reads it. It also declares the picture's\n * ADJUSTMENTS — the typed knobs a score may hold on it, `f.picture.*` —\n * which for the iframe picture are Look, Weather, Winter, Sun, Light,\n * Set and Build (`graph/adjust.gts`). A third kind of picture (a video, a\n * three.js scene in the same document) is another component with its own\n * spec and its own adjustments; the film never sees the difference.\n */\nimport Component from '@glimmer/component';\nimport { modifier } from 'ember-modifier';\n\nimport { IFRAME_PICTURE } from './graph/adjust.gts';\nimport type { FilmGrade, LookFx, Picture } from './types.ts';\n\n/** what a picture tells the film about itself, once */\nexport interface PictureSpec {\n /** the editorial accent on a dark frame, and on a light one (the scene's own palette when unset) */\n accent?: { dark: string; light?: string };\n /** the adjustments a score may hold on this picture, yielded as `f.picture.*` */\n adjustments: typeof IFRAME_PICTURE;\n /** where the picture's files live: `vo/<id>.mp3`, `luts/*.cube`, photographs */\n assets: string;\n /** how much of a frosted city a film frame can carry (the page's own is 0.30) */\n cityGlass?: number;\n /** how much a passing cloud thickens the air */\n cloudHaze?: number;\n /** how much each mood brightens the frame */\n gradeLum?: Record<string, number>;\n /** the moods, as numbers the glass can take */\n grades?: Record<string, FilmGrade>;\n /** how each named look is worn */\n lookFx?: Record<string, LookFx>;\n /** how much of a stock the picture wears when a shot names one */\n lutAmount?: number;\n /** how the door is lit: the hour and the key light, degrees */\n poster?: { light?: { az: number; el: number }; theme?: number };\n /** the rig's mid-height: `lookY` is world height minus this */\n rigMid?: number;\n /**\n * THE SEAMS THIS PICTURE CAN RUN IN ITS OWN GLASS, by name. A join that\n * names one is handed to `Picture.seam` with its progress instead of\n * being drawn as an overlay in this document — which is the only way a\n * transition can be a SHADER, and the only way it can composite in\n * light. Declared the way `adjustments` is: the actor owns the\n * vocabulary, the film hands it values and knows nothing else about it.\n */\n seams?: string[];\n /** told once when the picture arrives, before the door: what it should show */\n seat?: (picture: Picture) => void;\n /** the picture's page, mounted in an iframe */\n src: string;\n /** an already-fetched page, for hosts that cannot navigate an iframe to raw HTML */\n srcdoc?: string;\n /** the page's clock at which the subject stands whole */\n standing: number;\n /** the iframe's accessible name */\n title: string;\n /** how type standing in the scene is set */\n worldType?: { family?: string; track?: number; weight?: number };\n}\n\nexport interface IframePictureSignature {\n Args: Omit<PictureSpec, 'adjustments'> & {\n /** the film's registrar, yielded by its `<:picture>` block */\n register: (spec: PictureSpec | null) => void;\n };\n}\n\n/** a WebGL page in an iframe, addressed through the `Picture` port: both reference films' kind */\nexport class IframePicture extends Component<IframePictureSignature> {\n private pending = 0;\n\n private get spec(): PictureSpec {\n const a = this.args;\n return {\n accent: a.accent,\n adjustments: IFRAME_PICTURE,\n assets: a.assets,\n cityGlass: a.cityGlass,\n cloudHaze: a.cloudHaze,\n gradeLum: a.gradeLum,\n grades: a.grades,\n lookFx: a.lookFx,\n lutAmount: a.lutAmount,\n poster: a.poster,\n rigMid: a.rigMid,\n seams: a.seams,\n seat: a.seat,\n src: a.src,\n srcdoc: a.srcdoc,\n standing: a.standing,\n title: a.title,\n worldType: a.worldType,\n };\n }\n\n /** registers a frame after render, and again whenever an argument changes */\n private attach = modifier(() => {\n const spec = this.spec; // read here, so the modifier tracks every argument\n if (this.pending) {\n cancelAnimationFrame(this.pending);\n }\n this.pending = requestAnimationFrame(() => {\n this.pending = 0;\n this.args.register(spec);\n });\n return () => {\n if (this.pending) {\n cancelAnimationFrame(this.pending);\n this.pending = 0;\n }\n this.args.register(null);\n };\n });\n\n <template>\n <span hidden data-film-picture {{this.attach}}></span>\n </template>\n}\n"],"names":["IframePicture","Component","pending","spec","a","args","accent","adjustments","IFRAME_PICTURE","assets","cityGlass","cloudHaze","gradeLum","grades","lookFx","lutAmount","poster","rigMid","seams","seat","src","srcdoc","standing","title","worldType","attach","modifier","cancelAnimationFrame","requestAnimationFrame","register","setComponentTemplate","precompileTemplate","strictMode"],"mappings":";;;;;;AAwFA,kGACO,MAAMA,aAAA,SAAsBC,SAAA,CAAU;AACnCC,EAAAA,UAAU,CAAA;EAElB,IAAYC,OAAoB;AAC9B,IAAA,MAAMC,CAAA,GAAI,IAAI,CAACC,IAAI;IACnB,OAAO;MACLC,MAAA,EAAQF,EAAEE,MAAM;AAChBC,MAAAA,WAAA,EAAaC,cAAA;MACbC,MAAA,EAAQL,EAAEK,MAAM;MAChBC,SAAA,EAAWN,EAAEM,SAAS;MACtBC,SAAA,EAAWP,EAAEO,SAAS;MACtBC,QAAA,EAAUR,EAAEQ,QAAQ;MACpBC,MAAA,EAAQT,EAAES,MAAM;MAChBC,MAAA,EAAQV,EAAEU,MAAM;MAChBC,SAAA,EAAWX,EAAEW,SAAS;MACtBC,MAAA,EAAQZ,EAAEY,MAAM;MAChBC,MAAA,EAAQb,EAAEa,MAAM;MAChBC,KAAA,EAAOd,EAAEc,KAAK;MACdC,IAAA,EAAMf,EAAEe,IAAI;MACZC,GAAA,EAAKhB,EAAEgB,GAAG;MACVC,MAAA,EAAQjB,EAAEiB,MAAM;MAChBC,QAAA,EAAUlB,EAAEkB,QAAQ;MACpBC,KAAA,EAAOnB,EAAEmB,KAAK;MACdC,SAAA,EAAWpB,EAAEoB;KACf;AACF,EAAA;AAEA;EACQC,MAAA,GAASC,QAAA,CAAS,MAAA;AACxB,IAAA,MAAMvB,IAAA,GAAO,IAAI,CAACA,IAAI;IACtB,IAAI,IAAI,CAACD,OAAO,EAAE;AAChByB,MAAAA,oBAAA,CAAqB,IAAI,CAACzB,OAAO,CAAA;AACnC,IAAA;AACA,IAAA,IAAI,CAACA,OAAO,GAAG0B,qBAAA,CAAsB,MAAA;MACnC,IAAI,CAAC1B,OAAO,GAAG,CAAA;AACf,MAAA,IAAI,CAACG,IAAI,CAACwB,QAAQ,CAAC1B,IAAA,CAAA;AACrB,IAAA,CAAA,CAAA;AACA,IAAA,OAAO,MAAA;MACL,IAAI,IAAI,CAACD,OAAO,EAAE;AAChByB,QAAAA,oBAAA,CAAqB,IAAI,CAACzB,OAAO,CAAA;QACjC,IAAI,CAACA,OAAO,GAAG,CAAA;AACjB,MAAA;AACA,MAAA,IAAI,CAACG,IAAI,CAACwB,QAAQ,CAAC,IAAA,CAAA;IACrB,CAAA;AACF,EAAA,CAAA,CAAA;AAEA,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,wDAAA,EAEA;MAAAC,UAAA,EAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { get, concat, array } from '@ember/helper';
|
|
2
|
+
import Component from '@glimmer/component';
|
|
3
|
+
import { motion } from 'glimmer-motion';
|
|
4
|
+
import { Choreo } from '../choreo.js';
|
|
5
|
+
import { precompileTemplate } from '@ember/template-compilation';
|
|
6
|
+
import { setComponentTemplate } from '@ember/component';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* THE FRONT LAYER — the lower third, the title, the plate and the point,
|
|
10
|
+
* which are one component set four ways. Keyed on the beat, so every
|
|
11
|
+
* hand-off replays the delivery: the kicker rises, the word lands
|
|
12
|
+
* centre-out on an overshoot, the reading and the gloss follow, and the
|
|
13
|
+
* phrases arrive one by one against the measured read. A beat change
|
|
14
|
+
* drops the old type in one quick fall. All of it is score vocabulary.
|
|
15
|
+
*
|
|
16
|
+
* Each row is a PLANE, and the nesting is load-bearing: the wrapper is
|
|
17
|
+
* the plane and the film's loop drifts it, the paragraph inside is the
|
|
18
|
+
* type and Motion delivers it. One writer each.
|
|
19
|
+
*/ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
|
20
|
+
class Plate extends Component {
|
|
21
|
+
static {
|
|
22
|
+
setComponentTemplate(precompileTemplate("<Choreo @id=\"plate\" class=\"cf-type cf-{{@beat.mode}}\" as |n|>\n <span class=\"cf-ghost\" aria-hidden=\"true\">{{@chapterN}}</span>\n {{#if @rolling}}\n {{#each (array @beat) key=\"id\" as |b|}}\n <div class=\"cf-block is-{{b.mode}}\" {{@mount}}>\n {{#if b.kicker}}\n <div class=\"cf-plane\">\n <p class=\"cf-kicker\" {{motion id=\"kicker\" role=\"kick\"}}>\n <span>{{b.kicker}}</span>\n </p>\n </div>\n {{/if}}\n {{!-- when the beat has a MARK the term is already standing in\n the scene, so the front layer does not set it a second time --}}\n {{#unless b.mark}}\n {{#if b.kanji}}\n <div class=\"cf-plane is-glyph\" style={{@glyphFit}}>\n <p class=\"cf-kanji {{@glyphTone}}\" {{motion id=\"kanji\" role=\"glyph\"}}>\n {{b.kanji}}\n </p>\n </div>\n {{/if}}\n {{/unless}}\n {{#if b.romaji}}\n <div class=\"cf-plane\">\n <p class=\"cf-read\" {{motion id=\"read\" role=\"read\"}}>\n <span class=\"cf-romaji\">{{b.romaji}}</span>\n {{#if b.gloss}}\n <span class=\"cf-gloss\">{{b.gloss}}</span>\n {{/if}}\n </p>\n </div>\n {{/if}}\n {{!-- kinetic type, not a paragraph: each phrase is its own sprite\n with its own role, because a cue has a time --}}\n {{#each b.says as |say index|}}\n <div class=\"cf-plane\">\n <p class=\"cf-say\" {{motion id=(concat \"say\" index) role=(concat \"s\" index)}}><span class=\"cf-sayx sx-{{b.id}}-{{index}}\">{{say}}</span></p>\n </div>\n {{/each}}\n </div>\n {{/each}}\n {{/if}}\n\n {{!-- THE TYPE IS ITS OWN SCENE. Every element enters and leaves on one\n vocabulary and in one order: kicker, glyph, reading, then the lines\n against the voice; out as a wave in reverse. --}}\n <n.Parallel>\n <n.Tween @of={{n.inserted \"kick\"}} @delay={{get @typeAt 0}} @opacity={{array 0 1}} @y={{array 10 0}} @duration={{0.55}} @ease=\"easeOut\" />\n <n.Tween @of={{n.inserted \"glyph\"}} @by=\"character\" @order=\"center\" @stagger={{0.07}} @delay={{get @typeAt 1}} @opacity={{array 0 1}} @y={{array 16 0}} @duration={{0.9}} @ease=\"easeOut\" />\n <n.Tween @of={{n.inserted \"read\"}} @delay={{get @typeAt 2}} @opacity={{array 0 1}} @y={{array 8 0}} @duration={{0.62}} @ease=\"easeOut\" />\n <n.Tween @of={{n.inserted \"s0\"}} @delay={{get @sayAt 0}} @opacity={{array 0 1}} @y={{array 8 0}} @duration={{0.7}} @ease=\"easeOut\" />\n <n.Tween @of={{n.inserted \"s1\"}} @delay={{get @sayAt 1}} @opacity={{array 0 1}} @y={{array 8 0}} @duration={{0.7}} @ease=\"easeOut\" />\n <n.Tween @of={{n.inserted \"s2\"}} @delay={{get @sayAt 2}} @opacity={{array 0 1}} @y={{array 8 0}} @duration={{0.7}} @ease=\"easeOut\" />\n <n.Tween @of={{n.inserted \"s3\"}} @delay={{get @sayAt 3}} @opacity={{array 0 1}} @y={{array 8 0}} @duration={{0.7}} @ease=\"easeOut\" />\n <n.Tween @of={{array (n.removed \"s0\") (n.removed \"s1\") (n.removed \"s2\") (n.removed \"s3\")}} @opacity={{0}} @y={{array 0 -7}} @duration={{0.3}} @ease=\"easeIn\" />\n <n.Tween @of={{n.removed \"read\"}} @delay={{0.05}} @opacity={{0}} @y={{array 0 -7}} @duration={{0.3}} @ease=\"easeIn\" />\n <n.Tween @of={{n.removed \"glyph\"}} @delay={{0.1}} @opacity={{0}} @y={{array 0 -9}} @duration={{0.34}} @ease=\"easeIn\" />\n <n.Tween @of={{n.removed \"kick\"}} @delay={{0.16}} @opacity={{0}} @y={{array 0 -7}} @duration={{0.3}} @ease=\"easeIn\" />\n </n.Parallel>\n</Choreo>", {
|
|
23
|
+
strictMode: true,
|
|
24
|
+
scope: () => ({
|
|
25
|
+
Choreo,
|
|
26
|
+
array,
|
|
27
|
+
motion,
|
|
28
|
+
concat,
|
|
29
|
+
get
|
|
30
|
+
})
|
|
31
|
+
}), this);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { Plate };
|
|
36
|
+
//# sourceMappingURL=plate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plate.js","sources":["../../src/film/plate.gts"],"sourcesContent":["import { array, concat, get } from '@ember/helper';\nimport Component from '@glimmer/component';\nimport { motion } from 'glimmer-motion';\n\nimport { Choreo } from '../choreo.gts';\nimport type { Beat, ElementModifier } from './types.ts';\n\nexport interface PlateSignature {\n Args: {\n /** the beat being set */\n beat: Beat;\n /** the chapter's numeral, set enormous behind a plate */\n chapterN: string;\n /** `--cf-glyphs`: how many characters the word is, for the column's size */\n glyphFit: string;\n /** 'is-latin' | 'is-tall' | '' — the script's own setting */\n glyphTone: string;\n /** the film's own handle on the block, for the parallax and the fade */\n mount: ElementModifier;\n /** one pass behind the boot: content mounts on this so its entrance is a real insertion */\n rolling: boolean;\n /** when each of up to four phrases lands, seconds into the beat */\n sayAt: number[];\n /** when the kicker, the word and the reading land, seconds into the beat */\n typeAt: number[];\n };\n}\n\n/**\n * THE FRONT LAYER — the lower third, the title, the plate and the point,\n * which are one component set four ways. Keyed on the beat, so every\n * hand-off replays the delivery: the kicker rises, the word lands\n * centre-out on an overshoot, the reading and the gloss follow, and the\n * phrases arrive one by one against the measured read. A beat change\n * drops the old type in one quick fall. All of it is score vocabulary.\n *\n * Each row is a PLANE, and the nesting is load-bearing: the wrapper is\n * the plane and the film's loop drifts it, the paragraph inside is the\n * type and Motion delivers it. One writer each.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Plate extends Component<PlateSignature> {\n <template>\n <Choreo @id='plate' class='cf-type cf-{{@beat.mode}}' as |n|>\n <span class='cf-ghost' aria-hidden='true'>{{@chapterN}}</span>\n {{#if @rolling}}\n {{#each (array @beat) key='id' as |b|}}\n <div class='cf-block is-{{b.mode}}' {{@mount}}>\n {{#if b.kicker}}\n <div class='cf-plane'>\n <p class='cf-kicker' {{motion id='kicker' role='kick'}}>\n <span>{{b.kicker}}</span>\n </p>\n </div>\n {{/if}}\n {{! when the beat has a MARK the term is already standing in\n the scene, so the front layer does not set it a second time }}\n {{#unless b.mark}}\n {{#if b.kanji}}\n <div class='cf-plane is-glyph' style={{@glyphFit}}>\n <p\n class='cf-kanji {{@glyphTone}}'\n {{motion id='kanji' role='glyph'}}\n >\n {{b.kanji}}\n </p>\n </div>\n {{/if}}\n {{/unless}}\n {{#if b.romaji}}\n <div class='cf-plane'>\n <p class='cf-read' {{motion id='read' role='read'}}>\n <span class='cf-romaji'>{{b.romaji}}</span>\n {{#if b.gloss}}\n <span class='cf-gloss'>{{b.gloss}}</span>\n {{/if}}\n </p>\n </div>\n {{/if}}\n {{! kinetic type, not a paragraph: each phrase is its own sprite\n with its own role, because a cue has a time }}\n {{#each b.says as |say index|}}\n <div class='cf-plane'>\n <p\n class='cf-say'\n {{motion id=(concat 'say' index) role=(concat 's' index)}}\n ><span class='cf-sayx sx-{{b.id}}-{{index}}'>{{say}}</span></p>\n </div>\n {{/each}}\n </div>\n {{/each}}\n {{/if}}\n\n {{! THE TYPE IS ITS OWN SCENE. Every element enters and leaves on one\n vocabulary and in one order: kicker, glyph, reading, then the lines\n against the voice; out as a wave in reverse. }}\n <n.Parallel>\n <n.Tween\n @of={{n.inserted 'kick'}}\n @delay={{get @typeAt 0}}\n @opacity={{array 0 1}}\n @y={{array 10 0}}\n @duration={{0.55}}\n @ease='easeOut'\n />\n <n.Tween\n @of={{n.inserted 'glyph'}}\n @by='character'\n @order='center'\n @stagger={{0.07}}\n @delay={{get @typeAt 1}}\n @opacity={{array 0 1}}\n @y={{array 16 0}}\n @duration={{0.9}}\n @ease='easeOut'\n />\n <n.Tween\n @of={{n.inserted 'read'}}\n @delay={{get @typeAt 2}}\n @opacity={{array 0 1}}\n @y={{array 8 0}}\n @duration={{0.62}}\n @ease='easeOut'\n />\n <n.Tween\n @of={{n.inserted 's0'}}\n @delay={{get @sayAt 0}}\n @opacity={{array 0 1}}\n @y={{array 8 0}}\n @duration={{0.7}}\n @ease='easeOut'\n />\n <n.Tween\n @of={{n.inserted 's1'}}\n @delay={{get @sayAt 1}}\n @opacity={{array 0 1}}\n @y={{array 8 0}}\n @duration={{0.7}}\n @ease='easeOut'\n />\n <n.Tween\n @of={{n.inserted 's2'}}\n @delay={{get @sayAt 2}}\n @opacity={{array 0 1}}\n @y={{array 8 0}}\n @duration={{0.7}}\n @ease='easeOut'\n />\n <n.Tween\n @of={{n.inserted 's3'}}\n @delay={{get @sayAt 3}}\n @opacity={{array 0 1}}\n @y={{array 8 0}}\n @duration={{0.7}}\n @ease='easeOut'\n />\n <n.Tween\n @of={{array\n (n.removed 's0')\n (n.removed 's1')\n (n.removed 's2')\n (n.removed 's3')\n }}\n @opacity={{0}}\n @y={{array 0 -7}}\n @duration={{0.3}}\n @ease='easeIn'\n />\n <n.Tween\n @of={{n.removed 'read'}}\n @delay={{0.05}}\n @opacity={{0}}\n @y={{array 0 -7}}\n @duration={{0.3}}\n @ease='easeIn'\n />\n <n.Tween\n @of={{n.removed 'glyph'}}\n @delay={{0.1}}\n @opacity={{0}}\n @y={{array 0 -9}}\n @duration={{0.34}}\n @ease='easeIn'\n />\n <n.Tween\n @of={{n.removed 'kick'}}\n @delay={{0.16}}\n @opacity={{0}}\n @y={{array 0 -7}}\n @duration={{0.3}}\n @ease='easeIn'\n />\n </n.Parallel>\n </Choreo>\n </template>\n}\n"],"names":["Plate","Component","setComponentTemplate","precompileTemplate","strictMode","scope","Choreo","array","motion","concat","get"],"mappings":";;;;;;;AA4BA;;;;;;;;;;;AAWC;AAEM,MAAMA,cAAcC,SAAA,CAAU;AACnC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,gkHAAA,EAwJA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,MAAA;QAAAC,KAAA;QAAAC,MAAA;QAAAC,MAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
|