@kitsra/kavio-builder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +94 -0
- package/dist/index.d.ts +863 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1670 -0
- package/package.json +30 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1670 @@
|
|
|
1
|
+
import { validateComposition } from "@kitsra/kavio-schema";
|
|
2
|
+
const propPattern = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
|
|
3
|
+
const animatableProperties = ["opacity", "x", "y", "scale", "rotation"];
|
|
4
|
+
export class PropReference {
|
|
5
|
+
name;
|
|
6
|
+
metadata;
|
|
7
|
+
constructor(name, metadata) {
|
|
8
|
+
if (!propPattern.test(name)) {
|
|
9
|
+
throw new Error(`Invalid Kavio prop name "${name}".`);
|
|
10
|
+
}
|
|
11
|
+
this.name = name;
|
|
12
|
+
if (metadata !== undefined) {
|
|
13
|
+
this.metadata = metadata;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
toString() {
|
|
17
|
+
return interpolationFor(this.name);
|
|
18
|
+
}
|
|
19
|
+
toJSON() {
|
|
20
|
+
return interpolationFor(this.name);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class AssetReference {
|
|
24
|
+
id;
|
|
25
|
+
type;
|
|
26
|
+
definition;
|
|
27
|
+
constructor(id, type, src, options = {}) {
|
|
28
|
+
assertId(id, "asset");
|
|
29
|
+
this.id = id;
|
|
30
|
+
this.type = type;
|
|
31
|
+
this.definition = compactObject({
|
|
32
|
+
type,
|
|
33
|
+
src,
|
|
34
|
+
...options
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
toDefinition() {
|
|
38
|
+
return { ...this.definition };
|
|
39
|
+
}
|
|
40
|
+
toString() {
|
|
41
|
+
return this.id;
|
|
42
|
+
}
|
|
43
|
+
toJSON() {
|
|
44
|
+
return this.id;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class LayerBuilder {
|
|
48
|
+
layer;
|
|
49
|
+
constructor(layer) {
|
|
50
|
+
this.layer = layer;
|
|
51
|
+
}
|
|
52
|
+
animate(property, frames) {
|
|
53
|
+
return this.motion({ [property]: frames });
|
|
54
|
+
}
|
|
55
|
+
motion(frames) {
|
|
56
|
+
const existing = isRecord(this.layer.keyframes) ? this.layer.keyframes : {};
|
|
57
|
+
const next = { ...existing };
|
|
58
|
+
for (const property of animatableProperties) {
|
|
59
|
+
const propertyFrames = frames[property];
|
|
60
|
+
if (propertyFrames !== undefined) {
|
|
61
|
+
next[property] = propertyFrames.map((frame) => ({ ...frame }));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
this.layer.keyframes = next;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
transitionIn(definition) {
|
|
68
|
+
this.layer.transitionIn = { ...definition };
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
transitionOut(definition) {
|
|
72
|
+
this.layer.transitionOut = { ...definition };
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
enter(definition) {
|
|
76
|
+
return this.transitionIn(definition);
|
|
77
|
+
}
|
|
78
|
+
exit(definition) {
|
|
79
|
+
return this.transitionOut(definition);
|
|
80
|
+
}
|
|
81
|
+
toInput() {
|
|
82
|
+
return cloneAuthorObject(this.layer);
|
|
83
|
+
}
|
|
84
|
+
toJSON() {
|
|
85
|
+
return normalizeStandalone(this.layer);
|
|
86
|
+
}
|
|
87
|
+
get id() {
|
|
88
|
+
return String(this.layer.id);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export class TrackBuilder {
|
|
92
|
+
definition;
|
|
93
|
+
constructor(id, clips = []) {
|
|
94
|
+
assertId(id, "track");
|
|
95
|
+
this.definition = {
|
|
96
|
+
id,
|
|
97
|
+
clips: clips.map((clip) => cloneTrackClip(clip))
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
clip(id, options) {
|
|
101
|
+
this.definition.clips.push(trackClip(id, options));
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
toInput() {
|
|
105
|
+
return {
|
|
106
|
+
id: this.definition.id,
|
|
107
|
+
clips: this.definition.clips.map((clip) => cloneTrackClip(clip))
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
toJSON() {
|
|
111
|
+
return normalizeStandalone(this.toInput());
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export class VideoBuilder {
|
|
115
|
+
composition;
|
|
116
|
+
metadata;
|
|
117
|
+
propDefinitions = new Map();
|
|
118
|
+
assetDefinitions = new Map();
|
|
119
|
+
layers = [];
|
|
120
|
+
trackDefinitions = [];
|
|
121
|
+
audioTracks = [];
|
|
122
|
+
exportDefinitions = [];
|
|
123
|
+
constructor(composition, options = {}) {
|
|
124
|
+
this.composition = { ...composition };
|
|
125
|
+
if (options.metadata !== undefined) {
|
|
126
|
+
this.metadata = options.metadata;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
prop(reference) {
|
|
130
|
+
this.registerProp(reference);
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
props(...references) {
|
|
134
|
+
for (const reference of references) {
|
|
135
|
+
this.registerProp(reference);
|
|
136
|
+
}
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
asset(reference) {
|
|
140
|
+
this.registerAsset(reference);
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
assets(...references) {
|
|
144
|
+
for (const reference of references) {
|
|
145
|
+
this.registerAsset(reference);
|
|
146
|
+
}
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
add(...layers) {
|
|
150
|
+
for (const layer of layers) {
|
|
151
|
+
this.layers.push(layer.toInput());
|
|
152
|
+
}
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
addTrack(...tracks) {
|
|
156
|
+
for (const entry of tracks) {
|
|
157
|
+
this.trackDefinitions.push(entry instanceof TrackBuilder ? entry.toInput() : cloneTrack(entry));
|
|
158
|
+
}
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
tracks(...tracks) {
|
|
162
|
+
return this.addTrack(...tracks);
|
|
163
|
+
}
|
|
164
|
+
audio(options) {
|
|
165
|
+
const entry = compactObject({
|
|
166
|
+
...options,
|
|
167
|
+
id: options.id ?? (options.asset instanceof AssetReference ? options.asset.id : undefined)
|
|
168
|
+
});
|
|
169
|
+
this.audioTracks.push(entry);
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
addExport(definition) {
|
|
173
|
+
this.exportDefinitions.push(compactObject(definition));
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
exports(...definitions) {
|
|
177
|
+
for (const definition of definitions) {
|
|
178
|
+
this.addExport(definition);
|
|
179
|
+
}
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
toJSON() {
|
|
183
|
+
const props = this.normalizeProps();
|
|
184
|
+
const document = {
|
|
185
|
+
version: "0.1",
|
|
186
|
+
...(this.metadata === undefined ? {} : { metadata: this.normalize(this.metadata) }),
|
|
187
|
+
composition: this.normalize(this.composition),
|
|
188
|
+
...(Object.keys(props).length === 0 ? {} : { props }),
|
|
189
|
+
assets: this.normalizeAssets(),
|
|
190
|
+
layers: this.layers.map((layer) => this.normalize(layer)),
|
|
191
|
+
...(this.trackDefinitions.length === 0
|
|
192
|
+
? {}
|
|
193
|
+
: { tracks: this.trackDefinitions.map((track) => this.normalize(track)) }),
|
|
194
|
+
audio: this.audioTracks.map((track) => this.normalize(track)),
|
|
195
|
+
exports: this.exportDefinitions.map((definition) => this.normalize(definition))
|
|
196
|
+
};
|
|
197
|
+
return structuredClone(document);
|
|
198
|
+
}
|
|
199
|
+
validate() {
|
|
200
|
+
return validateComposition(this.toJSON());
|
|
201
|
+
}
|
|
202
|
+
registerAsset(reference) {
|
|
203
|
+
this.assetDefinitions.set(reference.id, reference.toDefinition());
|
|
204
|
+
}
|
|
205
|
+
registerProp(reference) {
|
|
206
|
+
if (reference.metadata === undefined) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const existing = this.propDefinitions.get(reference.name) ?? {};
|
|
210
|
+
this.propDefinitions.set(reference.name, { ...existing, ...reference.metadata });
|
|
211
|
+
}
|
|
212
|
+
normalizeProps() {
|
|
213
|
+
const output = {};
|
|
214
|
+
for (const [name, metadata] of this.propDefinitions) {
|
|
215
|
+
output[name] = this.normalize(metadata);
|
|
216
|
+
}
|
|
217
|
+
return output;
|
|
218
|
+
}
|
|
219
|
+
normalizeAssets() {
|
|
220
|
+
const output = {};
|
|
221
|
+
for (const [id, definition] of this.assetDefinitions) {
|
|
222
|
+
output[id] = this.normalize(definition);
|
|
223
|
+
}
|
|
224
|
+
return output;
|
|
225
|
+
}
|
|
226
|
+
normalize(value) {
|
|
227
|
+
if (value instanceof PropReference) {
|
|
228
|
+
this.registerProp(value);
|
|
229
|
+
return interpolationFor(value.name);
|
|
230
|
+
}
|
|
231
|
+
if (value instanceof AssetReference) {
|
|
232
|
+
this.registerAsset(value);
|
|
233
|
+
return value.id;
|
|
234
|
+
}
|
|
235
|
+
if (Array.isArray(value)) {
|
|
236
|
+
return value.map((item) => this.normalize(item));
|
|
237
|
+
}
|
|
238
|
+
if (isRecord(value)) {
|
|
239
|
+
const output = {};
|
|
240
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
241
|
+
if (nested !== undefined) {
|
|
242
|
+
output[key] = this.normalize(nested);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return output;
|
|
246
|
+
}
|
|
247
|
+
return value;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
export function video(composition, options) {
|
|
251
|
+
return new VideoBuilder(composition, options);
|
|
252
|
+
}
|
|
253
|
+
export function prop(name, metadata) {
|
|
254
|
+
return new PropReference(name, metadata);
|
|
255
|
+
}
|
|
256
|
+
export function validate(input) {
|
|
257
|
+
return validateComposition(input instanceof VideoBuilder ? input.toJSON() : input);
|
|
258
|
+
}
|
|
259
|
+
export const asset = {
|
|
260
|
+
video(id, src, options) {
|
|
261
|
+
return new AssetReference(id, "video", src, options);
|
|
262
|
+
},
|
|
263
|
+
image(id, src, options) {
|
|
264
|
+
return new AssetReference(id, "image", src, options);
|
|
265
|
+
},
|
|
266
|
+
audio(id, src, options) {
|
|
267
|
+
return new AssetReference(id, "audio", src, options);
|
|
268
|
+
},
|
|
269
|
+
font(id, src, options) {
|
|
270
|
+
return new AssetReference(id, "font", src, options);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
export function clip(id, options) {
|
|
274
|
+
return buildLayer(id, "video", options);
|
|
275
|
+
}
|
|
276
|
+
export function videoLayer(id, options) {
|
|
277
|
+
return clip(id, options);
|
|
278
|
+
}
|
|
279
|
+
export function image(id, options) {
|
|
280
|
+
return buildLayer(id, "image", options);
|
|
281
|
+
}
|
|
282
|
+
export function text(id, options) {
|
|
283
|
+
return buildLayer(id, "text", options);
|
|
284
|
+
}
|
|
285
|
+
export function shape(id, options) {
|
|
286
|
+
return buildLayer(id, "shape", { shape: "rect", ...options });
|
|
287
|
+
}
|
|
288
|
+
export function caption(id, options) {
|
|
289
|
+
return buildLayer(id, "caption", options);
|
|
290
|
+
}
|
|
291
|
+
export function trackClip(id, options) {
|
|
292
|
+
assertId(id, "track clip");
|
|
293
|
+
const definition = {
|
|
294
|
+
id,
|
|
295
|
+
layerId: layerIdFor(options.layerId),
|
|
296
|
+
startFrame: options.startFrame,
|
|
297
|
+
durationFrames: options.durationFrames
|
|
298
|
+
};
|
|
299
|
+
if (options.transitionFromPrevious !== undefined) {
|
|
300
|
+
definition.transitionFromPrevious = transitionSeries.fromPrevious(options.transitionFromPrevious);
|
|
301
|
+
}
|
|
302
|
+
return definition;
|
|
303
|
+
}
|
|
304
|
+
export function track(id, clips = []) {
|
|
305
|
+
return new TrackBuilder(id, clips);
|
|
306
|
+
}
|
|
307
|
+
export const layers = {
|
|
308
|
+
video: videoLayer,
|
|
309
|
+
clip,
|
|
310
|
+
image,
|
|
311
|
+
text,
|
|
312
|
+
shape,
|
|
313
|
+
caption
|
|
314
|
+
};
|
|
315
|
+
export const transition = {
|
|
316
|
+
fade(options) {
|
|
317
|
+
return transitionDefinition("fade", options);
|
|
318
|
+
},
|
|
319
|
+
slide(options) {
|
|
320
|
+
return transitionDefinition("slide", options);
|
|
321
|
+
},
|
|
322
|
+
wipe(options) {
|
|
323
|
+
return transitionDefinition("wipe", options);
|
|
324
|
+
},
|
|
325
|
+
crossfade(options) {
|
|
326
|
+
return transitionDefinition("crossfade", options);
|
|
327
|
+
},
|
|
328
|
+
zoom(options = { durationFrames: 12 }) {
|
|
329
|
+
return transitionDefinition("zoom", options);
|
|
330
|
+
},
|
|
331
|
+
push(options) {
|
|
332
|
+
return transitionDefinition("push", options);
|
|
333
|
+
},
|
|
334
|
+
spin(options = { durationFrames: 12 }) {
|
|
335
|
+
return transitionDefinition("spin", options);
|
|
336
|
+
},
|
|
337
|
+
rotate(options = { durationFrames: 12 }) {
|
|
338
|
+
return transitionDefinition("rotate", options);
|
|
339
|
+
},
|
|
340
|
+
flip(options = { durationFrames: 12 }) {
|
|
341
|
+
return transitionDefinition("flip", options);
|
|
342
|
+
},
|
|
343
|
+
blurDissolve(options = { durationFrames: 12 }) {
|
|
344
|
+
return transitionDefinition("blurDissolve", options);
|
|
345
|
+
},
|
|
346
|
+
colorDissolve(options = { durationFrames: 12 }) {
|
|
347
|
+
return transitionDefinition("colorDissolve", options);
|
|
348
|
+
},
|
|
349
|
+
dip(options = { durationFrames: 12 }) {
|
|
350
|
+
return transitionDefinition("dip", options);
|
|
351
|
+
},
|
|
352
|
+
iris(options = { durationFrames: 12 }) {
|
|
353
|
+
return transitionDefinition("iris", options);
|
|
354
|
+
},
|
|
355
|
+
stretch(options = { durationFrames: 12 }) {
|
|
356
|
+
return transitionDefinition("stretch", options);
|
|
357
|
+
},
|
|
358
|
+
squeeze(options = { durationFrames: 12 }) {
|
|
359
|
+
return transitionDefinition("squeeze", options);
|
|
360
|
+
},
|
|
361
|
+
clockWipe(options = { durationFrames: 12 }) {
|
|
362
|
+
return transitionDefinition("clockWipe", options);
|
|
363
|
+
},
|
|
364
|
+
barWipe(options = { durationFrames: 12 }) {
|
|
365
|
+
return transitionDefinition("barWipe", options);
|
|
366
|
+
},
|
|
367
|
+
gridWipe(options = { durationFrames: 12 }) {
|
|
368
|
+
return transitionDefinition("gridWipe", options);
|
|
369
|
+
},
|
|
370
|
+
tileReveal(options = { durationFrames: 12 }) {
|
|
371
|
+
return transitionDefinition("tileReveal", options);
|
|
372
|
+
},
|
|
373
|
+
radialBlur(options = { durationFrames: 12 }) {
|
|
374
|
+
return transitionDefinition("radialBlur", options);
|
|
375
|
+
},
|
|
376
|
+
zoomBlur(options = { durationFrames: 12 }) {
|
|
377
|
+
return transitionDefinition("zoomBlur", options);
|
|
378
|
+
},
|
|
379
|
+
bookFlip(options = { durationFrames: 12 }) {
|
|
380
|
+
return transitionDefinition("bookFlip", options);
|
|
381
|
+
},
|
|
382
|
+
pageCurlLite(options = { durationFrames: 12 }) {
|
|
383
|
+
return transitionDefinition("pageCurlLite", options);
|
|
384
|
+
},
|
|
385
|
+
skewSlide(options = { durationFrames: 12 }) {
|
|
386
|
+
return transitionDefinition("skewSlide", options);
|
|
387
|
+
},
|
|
388
|
+
expandMask(options = { durationFrames: 12 }) {
|
|
389
|
+
return transitionDefinition("expandMask", options);
|
|
390
|
+
},
|
|
391
|
+
letterboxReveal(options = { durationFrames: 12 }) {
|
|
392
|
+
return transitionDefinition("letterboxReveal", options);
|
|
393
|
+
},
|
|
394
|
+
filmFlash(options = { durationFrames: 6 }) {
|
|
395
|
+
return transitionDefinition("filmFlash", options);
|
|
396
|
+
},
|
|
397
|
+
cameraWhip(options = { durationFrames: 8 }) {
|
|
398
|
+
return transitionDefinition("cameraWhip", options);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
export const transitions = transition;
|
|
402
|
+
export const transitionSeries = {
|
|
403
|
+
fromPrevious(definition) {
|
|
404
|
+
if (isTransitionSeriesDefinition(definition)) {
|
|
405
|
+
return cloneTransitionSeriesDefinition(definition);
|
|
406
|
+
}
|
|
407
|
+
return transitionSeriesDefinition(definition);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
export const camera = {
|
|
411
|
+
kenBurns(options) {
|
|
412
|
+
const amount = cameraTravelAmount(options, 36);
|
|
413
|
+
const direction = options.direction ?? "right";
|
|
414
|
+
const frames = zoomMotion(options, 1, 1 + cameraIntensity(options, 0.08));
|
|
415
|
+
if (direction === "left" || direction === "right") {
|
|
416
|
+
frames.x = cameraAxisFrames({
|
|
417
|
+
durationFrames: options.durationFrames,
|
|
418
|
+
easing: options.easing,
|
|
419
|
+
axis: "x",
|
|
420
|
+
direction,
|
|
421
|
+
amount,
|
|
422
|
+
restingValue: options.restingX ?? 0,
|
|
423
|
+
subjectAnchor: options.subjectAnchor
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
else if (direction === "up" || direction === "down") {
|
|
427
|
+
frames.y = cameraAxisFrames({
|
|
428
|
+
durationFrames: options.durationFrames,
|
|
429
|
+
easing: options.easing,
|
|
430
|
+
axis: "y",
|
|
431
|
+
direction,
|
|
432
|
+
amount,
|
|
433
|
+
restingValue: options.restingY ?? 0,
|
|
434
|
+
subjectAnchor: options.subjectAnchor
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
return frames;
|
|
438
|
+
},
|
|
439
|
+
pushIn(options) {
|
|
440
|
+
return zoomMotion(options, options.fromScale ?? 1, options.toScale ?? 1 + cameraIntensity(options, 0.08));
|
|
441
|
+
},
|
|
442
|
+
pullBack(options) {
|
|
443
|
+
return zoomMotion(options, options.fromScale ?? 1 + cameraIntensity(options, 0.08), options.toScale ?? 1);
|
|
444
|
+
},
|
|
445
|
+
pan(options) {
|
|
446
|
+
const direction = options.direction ?? "left";
|
|
447
|
+
const amount = cameraTravelAmount(options, 72);
|
|
448
|
+
const scale = options.scale ?? 1 + cameraIntensity(options, 0.04);
|
|
449
|
+
const frames = {
|
|
450
|
+
x: cameraAxisFrames({
|
|
451
|
+
durationFrames: options.durationFrames,
|
|
452
|
+
easing: options.easing,
|
|
453
|
+
axis: "x",
|
|
454
|
+
direction,
|
|
455
|
+
amount,
|
|
456
|
+
restingValue: options.restingX ?? 0,
|
|
457
|
+
fromValue: options.fromX,
|
|
458
|
+
toValue: options.toX,
|
|
459
|
+
subjectAnchor: options.subjectAnchor
|
|
460
|
+
})
|
|
461
|
+
};
|
|
462
|
+
if (scale !== 1) {
|
|
463
|
+
frames.scale = cameraTrack(options.durationFrames, scale, scale);
|
|
464
|
+
}
|
|
465
|
+
return frames;
|
|
466
|
+
},
|
|
467
|
+
tilt(options) {
|
|
468
|
+
const direction = options.direction ?? "up";
|
|
469
|
+
const amount = cameraTravelAmount(options, 72);
|
|
470
|
+
const scale = options.scale ?? 1 + cameraIntensity(options, 0.04);
|
|
471
|
+
const frames = {
|
|
472
|
+
y: cameraAxisFrames({
|
|
473
|
+
durationFrames: options.durationFrames,
|
|
474
|
+
easing: options.easing,
|
|
475
|
+
axis: "y",
|
|
476
|
+
direction,
|
|
477
|
+
amount,
|
|
478
|
+
restingValue: options.restingY ?? 0,
|
|
479
|
+
fromValue: options.fromY,
|
|
480
|
+
toValue: options.toY,
|
|
481
|
+
subjectAnchor: options.subjectAnchor
|
|
482
|
+
})
|
|
483
|
+
};
|
|
484
|
+
if (scale !== 1) {
|
|
485
|
+
frames.scale = cameraTrack(options.durationFrames, scale, scale);
|
|
486
|
+
}
|
|
487
|
+
return frames;
|
|
488
|
+
},
|
|
489
|
+
parallax(options) {
|
|
490
|
+
const direction = options.direction ?? "right";
|
|
491
|
+
const amount = cameraTravelAmount(options, 48);
|
|
492
|
+
const frames = zoomMotion(options, options.fromScale ?? 1.02, options.toScale ?? 1.02 + cameraIntensity(options, 0.04));
|
|
493
|
+
if (direction === "left" || direction === "right") {
|
|
494
|
+
frames.x = cameraAxisFrames({
|
|
495
|
+
durationFrames: options.durationFrames,
|
|
496
|
+
easing: options.easing,
|
|
497
|
+
axis: "x",
|
|
498
|
+
direction,
|
|
499
|
+
amount,
|
|
500
|
+
restingValue: options.restingX ?? 0,
|
|
501
|
+
fromValue: options.fromX,
|
|
502
|
+
toValue: options.toX,
|
|
503
|
+
subjectAnchor: options.subjectAnchor
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
else if (direction === "up" || direction === "down") {
|
|
507
|
+
frames.y = cameraAxisFrames({
|
|
508
|
+
durationFrames: options.durationFrames,
|
|
509
|
+
easing: options.easing,
|
|
510
|
+
axis: "y",
|
|
511
|
+
direction,
|
|
512
|
+
amount,
|
|
513
|
+
restingValue: options.restingY ?? 0,
|
|
514
|
+
fromValue: options.fromY,
|
|
515
|
+
toValue: options.toY,
|
|
516
|
+
subjectAnchor: options.subjectAnchor
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
return frames;
|
|
520
|
+
},
|
|
521
|
+
orbitLite(options) {
|
|
522
|
+
const endFrame = cameraEndFrame(options.durationFrames);
|
|
523
|
+
const middleFrame = Math.floor(endFrame / 2);
|
|
524
|
+
const amount = cameraTravelAmount(options, 42);
|
|
525
|
+
const halfAmount = amount / 2;
|
|
526
|
+
const verticalAmount = cameraTravelAmount({ ...options, amount: options.verticalAmount ?? 18 }, 18);
|
|
527
|
+
const rotationAmount = options.rotationAmount ?? 1.8 * (1 + cameraIntensity(options, 0));
|
|
528
|
+
const directionSign = (options.direction ?? "right") === "left" ? -1 : 1;
|
|
529
|
+
const restingX = options.restingX ?? 0;
|
|
530
|
+
const restingY = options.restingY ?? 0;
|
|
531
|
+
const restingRotation = options.restingRotation ?? 0;
|
|
532
|
+
const frames = {
|
|
533
|
+
x: cameraThreePointTrack(options.durationFrames, restingX - directionSign * halfAmount, restingX, restingX + directionSign * halfAmount, options.easing),
|
|
534
|
+
scale: cameraThreePointTrack(options.durationFrames, options.fromScale ?? 1.02, Math.max(options.fromScale ?? 1.02, options.toScale ?? 1.05), options.toScale ?? 1.02, options.easing),
|
|
535
|
+
rotation: cameraTrack(options.durationFrames, restingRotation - directionSign * rotationAmount, restingRotation + directionSign * rotationAmount, options.easing)
|
|
536
|
+
};
|
|
537
|
+
if (endFrame > 1 && verticalAmount !== 0) {
|
|
538
|
+
frames.y = [
|
|
539
|
+
compactObject({ frame: 0, value: restingY + verticalAmount / 2, easing: options.easing }),
|
|
540
|
+
compactObject({ frame: middleFrame, value: restingY - verticalAmount / 2, easing: options.easing }),
|
|
541
|
+
{ frame: endFrame, value: restingY + verticalAmount / 2 }
|
|
542
|
+
];
|
|
543
|
+
}
|
|
544
|
+
return frames;
|
|
545
|
+
},
|
|
546
|
+
handheld(options) {
|
|
547
|
+
const amount = cameraTravelAmount(options, 8);
|
|
548
|
+
const rotationAmount = options.rotationAmount ?? 0.8 * (1 + cameraIntensity(options, 0));
|
|
549
|
+
const scale = options.scale ?? 1 + cameraIntensity(options, 0.02);
|
|
550
|
+
const frames = {
|
|
551
|
+
x: cameraJitterTrack({
|
|
552
|
+
durationFrames: options.durationFrames,
|
|
553
|
+
seed: options.seed ?? 1,
|
|
554
|
+
intervalFrames: options.intervalFrames ?? 6,
|
|
555
|
+
restingValue: options.restingX ?? 0,
|
|
556
|
+
amount,
|
|
557
|
+
easing: options.easing ?? "inOutQuad"
|
|
558
|
+
}),
|
|
559
|
+
y: cameraJitterTrack({
|
|
560
|
+
durationFrames: options.durationFrames,
|
|
561
|
+
seed: (options.seed ?? 1) + 101,
|
|
562
|
+
intervalFrames: options.intervalFrames ?? 6,
|
|
563
|
+
restingValue: options.restingY ?? 0,
|
|
564
|
+
amount: amount * 0.65,
|
|
565
|
+
easing: options.easing ?? "inOutQuad"
|
|
566
|
+
}),
|
|
567
|
+
rotation: cameraJitterTrack({
|
|
568
|
+
durationFrames: options.durationFrames,
|
|
569
|
+
seed: (options.seed ?? 1) + 211,
|
|
570
|
+
intervalFrames: options.intervalFrames ?? 6,
|
|
571
|
+
restingValue: options.restingRotation ?? 0,
|
|
572
|
+
amount: rotationAmount,
|
|
573
|
+
easing: options.easing ?? "inOutQuad"
|
|
574
|
+
})
|
|
575
|
+
};
|
|
576
|
+
if (scale !== 1) {
|
|
577
|
+
frames.scale = cameraTrack(options.durationFrames, scale, scale);
|
|
578
|
+
}
|
|
579
|
+
return frames;
|
|
580
|
+
},
|
|
581
|
+
crashZoom(options) {
|
|
582
|
+
const direction = options.direction ?? "in";
|
|
583
|
+
const intensityValue = cameraIntensity(options, 0.28);
|
|
584
|
+
const fromScale = options.fromScale ?? (direction === "in" ? 1 : 1 + intensityValue);
|
|
585
|
+
const toScale = options.toScale ?? (direction === "in" ? 1 + intensityValue : 1);
|
|
586
|
+
const overshootDirection = direction === "in" ? 1 : -1;
|
|
587
|
+
const overshootScale = options.overshootScale ?? toScale + overshootDirection * intensityValue * 0.2;
|
|
588
|
+
return {
|
|
589
|
+
scale: cameraCrashZoomTrack({
|
|
590
|
+
durationFrames: options.durationFrames,
|
|
591
|
+
easing: options.easing ?? "outCubic",
|
|
592
|
+
fromScale,
|
|
593
|
+
overshootScale,
|
|
594
|
+
toScale,
|
|
595
|
+
impactFrame: options.impactFrame
|
|
596
|
+
})
|
|
597
|
+
};
|
|
598
|
+
},
|
|
599
|
+
dollyZoomLite(options) {
|
|
600
|
+
const direction = options.direction ?? "out";
|
|
601
|
+
const intensityValue = cameraIntensity(options, 0.12);
|
|
602
|
+
const fromScale = options.fromScale ?? (direction === "out" ? 1 + intensityValue : 1);
|
|
603
|
+
const toScale = options.toScale ?? (direction === "out" ? 1 : 1 + intensityValue);
|
|
604
|
+
const amount = cameraTravelAmount(options, 28);
|
|
605
|
+
const xBias = anchorBias(options.subjectAnchor, "x");
|
|
606
|
+
const yBias = anchorBias(options.subjectAnchor, "y");
|
|
607
|
+
const directionSign = direction === "out" ? 1 : -1;
|
|
608
|
+
const frames = zoomMotion(options, fromScale, toScale);
|
|
609
|
+
if (options.fromX !== undefined || options.toX !== undefined || xBias !== 0) {
|
|
610
|
+
const restingX = options.restingX ?? 0;
|
|
611
|
+
frames.x = cameraTrack(options.durationFrames, options.fromX ?? restingX - xBias * amount * directionSign, options.toX ?? restingX + xBias * amount * directionSign, options.easing);
|
|
612
|
+
}
|
|
613
|
+
if (options.fromY !== undefined || options.toY !== undefined || yBias !== 0) {
|
|
614
|
+
const restingY = options.restingY ?? 0;
|
|
615
|
+
frames.y = cameraTrack(options.durationFrames, options.fromY ?? restingY - yBias * amount * directionSign, options.toY ?? restingY + yBias * amount * directionSign, options.easing);
|
|
616
|
+
}
|
|
617
|
+
return frames;
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
export const cinematic = {
|
|
621
|
+
zoomPush(options = {}) {
|
|
622
|
+
const durationFrames = options.durationFrames ?? 14;
|
|
623
|
+
return compactObject({
|
|
624
|
+
transitionIn: transition.zoom({
|
|
625
|
+
durationFrames,
|
|
626
|
+
amount: options.intensity ?? 0.18,
|
|
627
|
+
easing: options.easing ?? "outCubic"
|
|
628
|
+
}),
|
|
629
|
+
transitionOut: transition.push({
|
|
630
|
+
direction: options.direction ?? "left",
|
|
631
|
+
durationFrames,
|
|
632
|
+
easing: options.easing ?? "inCubic"
|
|
633
|
+
})
|
|
634
|
+
});
|
|
635
|
+
},
|
|
636
|
+
whipPan(options = {}) {
|
|
637
|
+
return compactObject({
|
|
638
|
+
transitionIn: transition.push({
|
|
639
|
+
direction: options.direction ?? "left",
|
|
640
|
+
durationFrames: options.durationFrames ?? 8,
|
|
641
|
+
easing: options.easing ?? "inOutCubic",
|
|
642
|
+
intensity: options.intensity
|
|
643
|
+
})
|
|
644
|
+
});
|
|
645
|
+
},
|
|
646
|
+
filmFlash(options = {}) {
|
|
647
|
+
return compactObject({
|
|
648
|
+
transitionIn: transition.colorDissolve({
|
|
649
|
+
color: options.color ?? "#ffffff",
|
|
650
|
+
durationFrames: options.durationFrames ?? 6,
|
|
651
|
+
amount: options.intensity ?? 1,
|
|
652
|
+
easing: options.easing ?? "outQuad"
|
|
653
|
+
})
|
|
654
|
+
});
|
|
655
|
+
},
|
|
656
|
+
dreamyBlur(options = {}) {
|
|
657
|
+
return compactObject({
|
|
658
|
+
transitionIn: transition.blurDissolve({
|
|
659
|
+
durationFrames: options.durationFrames ?? 18,
|
|
660
|
+
amount: options.intensity ?? 18,
|
|
661
|
+
easing: options.easing ?? "outCubic"
|
|
662
|
+
})
|
|
663
|
+
});
|
|
664
|
+
},
|
|
665
|
+
broadcastDip(options = {}) {
|
|
666
|
+
return compactObject({
|
|
667
|
+
transitionIn: transition.dip({
|
|
668
|
+
color: options.color ?? "#05070a",
|
|
669
|
+
durationFrames: options.durationFrames ?? 10,
|
|
670
|
+
amount: options.intensity ?? 1,
|
|
671
|
+
easing: options.easing ?? "inOutCubic"
|
|
672
|
+
})
|
|
673
|
+
});
|
|
674
|
+
},
|
|
675
|
+
irisOpen(options = {}) {
|
|
676
|
+
return compactObject({
|
|
677
|
+
transitionIn: transition.iris({
|
|
678
|
+
shape: options.shape ?? "circle",
|
|
679
|
+
durationFrames: options.durationFrames ?? 16,
|
|
680
|
+
intensity: options.intensity,
|
|
681
|
+
easing: options.easing ?? "outCubic"
|
|
682
|
+
})
|
|
683
|
+
});
|
|
684
|
+
},
|
|
685
|
+
flipCard(options = {}) {
|
|
686
|
+
return compactObject({
|
|
687
|
+
transitionIn: transition.flip({
|
|
688
|
+
axis: options.axis ?? "y",
|
|
689
|
+
direction: options.direction,
|
|
690
|
+
durationFrames: options.durationFrames ?? 14,
|
|
691
|
+
amount: options.intensity ?? 90,
|
|
692
|
+
easing: options.easing ?? "outCubic"
|
|
693
|
+
})
|
|
694
|
+
});
|
|
695
|
+
},
|
|
696
|
+
glitchCut(options = {}) {
|
|
697
|
+
const durationFrames = options.durationFrames ?? 8;
|
|
698
|
+
const endFrame = cameraEndFrame(durationFrames);
|
|
699
|
+
const direction = options.direction ?? "left";
|
|
700
|
+
const directionSign = direction === "right" || direction === "down" ? 1 : -1;
|
|
701
|
+
const axis = direction === "up" || direction === "down" ? "y" : "x";
|
|
702
|
+
const offset = roundMotionValue((options.intensity ?? 1) * 14 * directionSign);
|
|
703
|
+
const jitter = roundMotionValue((seedToUnit(normalizeSeed(options.seed ?? 17)) * 2 - 1) * 6 * (options.intensity ?? 1));
|
|
704
|
+
const settleFrame = Math.min(endFrame, 3);
|
|
705
|
+
return compactObject({
|
|
706
|
+
transitionIn: transition.skewSlide({
|
|
707
|
+
direction,
|
|
708
|
+
durationFrames,
|
|
709
|
+
intensity: 12 * (options.intensity ?? 1),
|
|
710
|
+
easing: options.easing ?? "outExpo"
|
|
711
|
+
}),
|
|
712
|
+
keyframes: compactObject({
|
|
713
|
+
[axis]: keyframes([
|
|
714
|
+
{ frame: 0, value: offset, timing: timing.steps({ steps: 2, direction: "end" }) },
|
|
715
|
+
{ frame: 1, value: roundMotionValue(-offset * 0.45 + jitter), timing: timing.steps({ steps: 2, direction: "end" }) },
|
|
716
|
+
{ frame: settleFrame, value: 0, easing: options.easing ?? "outExpo" }
|
|
717
|
+
]),
|
|
718
|
+
opacity: keyframes([
|
|
719
|
+
{ frame: 0, value: 0.72, timing: timing.steps({ steps: 2, direction: "end" }) },
|
|
720
|
+
{ frame: settleFrame, value: 1 }
|
|
721
|
+
])
|
|
722
|
+
})
|
|
723
|
+
});
|
|
724
|
+
},
|
|
725
|
+
lightLeak(options = {}) {
|
|
726
|
+
const durationFrames = options.durationFrames ?? 14;
|
|
727
|
+
const easingValue = options.easing ?? "outQuad";
|
|
728
|
+
const drift = roundMotionValue(10 * (options.intensity ?? 1));
|
|
729
|
+
return compactObject({
|
|
730
|
+
transitionIn: transition.colorDissolve({
|
|
731
|
+
color: options.color ?? "#fff1b8",
|
|
732
|
+
durationFrames,
|
|
733
|
+
amount: options.intensity ?? 0.72,
|
|
734
|
+
easing: easingValue
|
|
735
|
+
}),
|
|
736
|
+
keyframes: compactObject({
|
|
737
|
+
x: cameraTrack(durationFrames, -drift, drift, easingValue),
|
|
738
|
+
opacity: keyframes([
|
|
739
|
+
[0, 0.82, easingValue],
|
|
740
|
+
[Math.max(1, durationFrames - 1), 1]
|
|
741
|
+
])
|
|
742
|
+
})
|
|
743
|
+
});
|
|
744
|
+
},
|
|
745
|
+
kenBurns(options = {}) {
|
|
746
|
+
const durationFrames = options.durationFrames ?? 90;
|
|
747
|
+
const subjectStart = options.subject === undefined
|
|
748
|
+
? undefined
|
|
749
|
+
: options.easing === undefined
|
|
750
|
+
? { frame: 0, x: options.subject.fromX, y: options.subject.fromY }
|
|
751
|
+
: { frame: 0, x: options.subject.fromX, y: options.subject.fromY, easing: options.easing };
|
|
752
|
+
const subjectCropOptions = options.subject === undefined
|
|
753
|
+
? undefined
|
|
754
|
+
: {
|
|
755
|
+
keyframes: [subjectStart, { frame: durationFrames, x: options.subject.toX, y: options.subject.toY }],
|
|
756
|
+
...(options.subject.smoothingFrames === undefined ? {} : { smoothingFrames: options.subject.smoothingFrames }),
|
|
757
|
+
...(options.subject.source === undefined ? {} : { source: options.subject.source })
|
|
758
|
+
};
|
|
759
|
+
return compactObject({
|
|
760
|
+
keyframes: compactObject({
|
|
761
|
+
scale: keyframes([
|
|
762
|
+
[0, options.fromScale ?? 1.04, options.easing ?? "outCubic"],
|
|
763
|
+
[durationFrames, options.toScale ?? 1.14]
|
|
764
|
+
]),
|
|
765
|
+
x: options.fromX === undefined && options.toX === undefined
|
|
766
|
+
? undefined
|
|
767
|
+
: keyframes([
|
|
768
|
+
[0, options.fromX ?? 0, options.easing ?? "outCubic"],
|
|
769
|
+
[durationFrames, options.toX ?? 0]
|
|
770
|
+
]),
|
|
771
|
+
y: options.fromY === undefined && options.toY === undefined
|
|
772
|
+
? undefined
|
|
773
|
+
: keyframes([
|
|
774
|
+
[0, options.fromY ?? 0, options.easing ?? "outCubic"],
|
|
775
|
+
[durationFrames, options.toY ?? 0]
|
|
776
|
+
])
|
|
777
|
+
}),
|
|
778
|
+
crop: subjectCropOptions === undefined ? undefined : crop.subject(subjectCropOptions)
|
|
779
|
+
});
|
|
780
|
+
},
|
|
781
|
+
logoSting(options = {}) {
|
|
782
|
+
const durationFrames = options.durationFrames ?? 18;
|
|
783
|
+
const easingValue = options.easing ?? "outBack";
|
|
784
|
+
const rotationDirection = options.direction === "right" || options.direction === "down" ? 1 : -1;
|
|
785
|
+
return compactObject({
|
|
786
|
+
transitionIn: transition.zoom({
|
|
787
|
+
durationFrames: Math.max(1, Math.round(durationFrames * 0.6)),
|
|
788
|
+
amount: options.intensity ?? 0.12,
|
|
789
|
+
easing: easingValue
|
|
790
|
+
}),
|
|
791
|
+
transitionOut: transition.fade({
|
|
792
|
+
durationFrames: Math.max(1, Math.round(durationFrames * 0.45)),
|
|
793
|
+
easing: "inCubic"
|
|
794
|
+
}),
|
|
795
|
+
keyframes: compactObject({
|
|
796
|
+
scale: cameraThreePointTrack(durationFrames, 0.92, 1 + cameraIntensity(options, 0.04), 1, easingValue),
|
|
797
|
+
rotation: cameraTrack(durationFrames, rotationDirection * -2, 0, options.easing ?? "outCubic")
|
|
798
|
+
})
|
|
799
|
+
});
|
|
800
|
+
},
|
|
801
|
+
productReveal(options = {}) {
|
|
802
|
+
const durationFrames = options.durationFrames ?? 24;
|
|
803
|
+
const easingValue = options.easing ?? "outCubic";
|
|
804
|
+
return compactObject({
|
|
805
|
+
transitionIn: transition.wipe({
|
|
806
|
+
direction: options.direction ?? "up",
|
|
807
|
+
durationFrames,
|
|
808
|
+
easing: easingValue
|
|
809
|
+
}),
|
|
810
|
+
keyframes: compactObject({
|
|
811
|
+
scale: cameraTrack(durationFrames, 0.96, 1 + cameraIntensity(options, 0.02), easingValue),
|
|
812
|
+
opacity: entranceOpacityKeyframes(Math.max(1, Math.round(durationFrames * 0.6)), easingValue)
|
|
813
|
+
})
|
|
814
|
+
});
|
|
815
|
+
},
|
|
816
|
+
socialHook(options = {}) {
|
|
817
|
+
const durationFrames = options.durationFrames ?? 16;
|
|
818
|
+
const easingValue = options.easing ?? "outCubic";
|
|
819
|
+
return compactObject({
|
|
820
|
+
transitionIn: transition.colorDissolve({
|
|
821
|
+
color: options.color ?? "#ffffff",
|
|
822
|
+
durationFrames: Math.max(1, Math.round(durationFrames * 0.45)),
|
|
823
|
+
amount: options.intensity ?? 0.7,
|
|
824
|
+
easing: "outQuad"
|
|
825
|
+
}),
|
|
826
|
+
transitionOut: transition.push({
|
|
827
|
+
direction: options.direction ?? "left",
|
|
828
|
+
durationFrames: Math.max(1, Math.round(durationFrames * 0.5)),
|
|
829
|
+
easing: "inCubic"
|
|
830
|
+
}),
|
|
831
|
+
keyframes: compactObject({
|
|
832
|
+
scale: cameraCrashZoomTrack({
|
|
833
|
+
durationFrames,
|
|
834
|
+
easing: easingValue,
|
|
835
|
+
fromScale: 1 + cameraIntensity(options, 0.16),
|
|
836
|
+
overshootScale: 0.98,
|
|
837
|
+
toScale: 1
|
|
838
|
+
})
|
|
839
|
+
})
|
|
840
|
+
});
|
|
841
|
+
},
|
|
842
|
+
titleSequence(options = {}) {
|
|
843
|
+
const durationFrames = options.durationFrames ?? 30;
|
|
844
|
+
const easingValue = options.easing ?? "outCubic";
|
|
845
|
+
return compactObject({
|
|
846
|
+
transitionIn: transition.slide({
|
|
847
|
+
direction: options.direction ?? "up",
|
|
848
|
+
durationFrames,
|
|
849
|
+
easing: easingValue
|
|
850
|
+
}),
|
|
851
|
+
transitionOut: transition.slide({
|
|
852
|
+
direction: options.exitDirection ?? "down",
|
|
853
|
+
durationFrames: Math.max(1, Math.round(durationFrames * 0.55)),
|
|
854
|
+
easing: "inCubic"
|
|
855
|
+
}),
|
|
856
|
+
keyframes: compactObject({
|
|
857
|
+
opacity: entranceOpacityKeyframes(durationFrames, easingValue)
|
|
858
|
+
})
|
|
859
|
+
});
|
|
860
|
+
},
|
|
861
|
+
endCard(options = {}) {
|
|
862
|
+
const durationFrames = options.durationFrames ?? 20;
|
|
863
|
+
const easingValue = options.easing ?? "outCubic";
|
|
864
|
+
const horizontalDirection = options.direction === "left" || options.direction === "right" ? options.direction : undefined;
|
|
865
|
+
const verticalDirection = options.direction === "up" || options.direction === "down" ? options.direction : undefined;
|
|
866
|
+
return compactObject({
|
|
867
|
+
transitionIn: transition.dip({
|
|
868
|
+
color: options.color ?? "#05070a",
|
|
869
|
+
durationFrames,
|
|
870
|
+
amount: options.intensity ?? 1,
|
|
871
|
+
easing: "inOutCubic"
|
|
872
|
+
}),
|
|
873
|
+
transitionOut: transition.fade({
|
|
874
|
+
durationFrames: Math.max(1, Math.round(durationFrames * 0.5)),
|
|
875
|
+
easing: "inCubic"
|
|
876
|
+
}),
|
|
877
|
+
keyframes: compactObject({
|
|
878
|
+
scale: cameraTrack(durationFrames, 0.98, 1, easingValue),
|
|
879
|
+
x: horizontalDirection === undefined
|
|
880
|
+
? undefined
|
|
881
|
+
: cameraAxisFrames({
|
|
882
|
+
durationFrames,
|
|
883
|
+
easing: easingValue,
|
|
884
|
+
axis: "x",
|
|
885
|
+
direction: horizontalDirection,
|
|
886
|
+
amount: 20 * (1 + cameraIntensity(options, 0)),
|
|
887
|
+
restingValue: 0
|
|
888
|
+
}),
|
|
889
|
+
y: verticalDirection === undefined
|
|
890
|
+
? undefined
|
|
891
|
+
: cameraAxisFrames({
|
|
892
|
+
durationFrames,
|
|
893
|
+
easing: easingValue,
|
|
894
|
+
axis: "y",
|
|
895
|
+
direction: verticalDirection,
|
|
896
|
+
amount: 20 * (1 + cameraIntensity(options, 0)),
|
|
897
|
+
restingValue: 0
|
|
898
|
+
})
|
|
899
|
+
})
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
export const cinematicPresets = cinematic;
|
|
904
|
+
function textMotionPreset(type, options, defaults) {
|
|
905
|
+
return {
|
|
906
|
+
textMotion: compactObject({
|
|
907
|
+
type,
|
|
908
|
+
split: options.split ?? defaults.split,
|
|
909
|
+
durationFrames: options.durationFrames ?? defaults.durationFrames,
|
|
910
|
+
easing: options.easing ?? defaults.easing,
|
|
911
|
+
staggerFrames: options.staggerFrames ?? defaults.staggerFrames,
|
|
912
|
+
origin: options.origin,
|
|
913
|
+
seed: options.seed,
|
|
914
|
+
preserveLayout: options.preserveLayout ?? true,
|
|
915
|
+
restingBox: options.restingBox,
|
|
916
|
+
direction: options.direction,
|
|
917
|
+
amount: options.amount,
|
|
918
|
+
intensity: options.intensity,
|
|
919
|
+
color: options.color
|
|
920
|
+
})
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
export const textMotion = {
|
|
924
|
+
rise(options = {}) {
|
|
925
|
+
const durationFrames = options.durationFrames ?? 14;
|
|
926
|
+
const easingValue = options.easing ?? "outCubic";
|
|
927
|
+
return {
|
|
928
|
+
transitionIn: transition.slide({
|
|
929
|
+
direction: options.direction ?? "up",
|
|
930
|
+
durationFrames,
|
|
931
|
+
easing: easingValue
|
|
932
|
+
}),
|
|
933
|
+
keyframes: {
|
|
934
|
+
opacity: entranceOpacityKeyframes(durationFrames, easingValue)
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
},
|
|
938
|
+
blurIn(options = {}) {
|
|
939
|
+
return {
|
|
940
|
+
transitionIn: transition.blurDissolve({
|
|
941
|
+
durationFrames: options.durationFrames ?? 12,
|
|
942
|
+
amount: options.amount,
|
|
943
|
+
intensity: options.intensity,
|
|
944
|
+
easing: options.easing ?? "outCubic"
|
|
945
|
+
})
|
|
946
|
+
};
|
|
947
|
+
},
|
|
948
|
+
typeOn(options = {}) {
|
|
949
|
+
return textMotionPreset("typeOn", options, {
|
|
950
|
+
split: "char",
|
|
951
|
+
durationFrames: 18,
|
|
952
|
+
easing: "linear",
|
|
953
|
+
staggerFrames: 1
|
|
954
|
+
});
|
|
955
|
+
},
|
|
956
|
+
cascade(options = {}) {
|
|
957
|
+
return textMotionPreset("cascade", options, {
|
|
958
|
+
split: "word",
|
|
959
|
+
durationFrames: 14,
|
|
960
|
+
easing: "outCubic",
|
|
961
|
+
staggerFrames: 2
|
|
962
|
+
});
|
|
963
|
+
},
|
|
964
|
+
scramble(options = {}) {
|
|
965
|
+
return textMotionPreset("scramble", options, {
|
|
966
|
+
split: "char",
|
|
967
|
+
durationFrames: 18,
|
|
968
|
+
easing: "outCubic",
|
|
969
|
+
staggerFrames: 1
|
|
970
|
+
});
|
|
971
|
+
},
|
|
972
|
+
highlightSweep(options = {}) {
|
|
973
|
+
return textMotionPreset("highlightSweep", options, {
|
|
974
|
+
split: "word",
|
|
975
|
+
durationFrames: 18,
|
|
976
|
+
easing: "outCubic",
|
|
977
|
+
staggerFrames: 0
|
|
978
|
+
});
|
|
979
|
+
},
|
|
980
|
+
trackingIn(options = {}) {
|
|
981
|
+
return textMotionPreset("trackingIn", options, {
|
|
982
|
+
split: "char",
|
|
983
|
+
durationFrames: 16,
|
|
984
|
+
easing: "outCubic",
|
|
985
|
+
staggerFrames: 1
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
export const textMotions = textMotion;
|
|
990
|
+
export const effect = {};
|
|
991
|
+
export const presetNamespaces = {
|
|
992
|
+
transition,
|
|
993
|
+
cinematic,
|
|
994
|
+
textMotion,
|
|
995
|
+
camera,
|
|
996
|
+
effect
|
|
997
|
+
};
|
|
998
|
+
export function keyframes(frames) {
|
|
999
|
+
return frames.map((frame) => {
|
|
1000
|
+
if (isKeyframeTuple(frame)) {
|
|
1001
|
+
const [frameNumber, value, ease] = frame;
|
|
1002
|
+
return compactObject({
|
|
1003
|
+
frame: frameNumber,
|
|
1004
|
+
value,
|
|
1005
|
+
easing: ease
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
return compactObject({
|
|
1009
|
+
frame: frame.frame,
|
|
1010
|
+
value: frame.value,
|
|
1011
|
+
easing: frame.easing,
|
|
1012
|
+
timing: frame.timing
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
export const easing = {
|
|
1017
|
+
linear: "linear",
|
|
1018
|
+
inQuad: "inQuad",
|
|
1019
|
+
outQuad: "outQuad",
|
|
1020
|
+
inOutQuad: "inOutQuad",
|
|
1021
|
+
inCubic: "inCubic",
|
|
1022
|
+
outCubic: "outCubic",
|
|
1023
|
+
inOutCubic: "inOutCubic",
|
|
1024
|
+
inCirc: "inCirc",
|
|
1025
|
+
outCirc: "outCirc",
|
|
1026
|
+
inOutCirc: "inOutCirc",
|
|
1027
|
+
inExpo: "inExpo",
|
|
1028
|
+
outExpo: "outExpo",
|
|
1029
|
+
inOutExpo: "inOutExpo",
|
|
1030
|
+
anticipate: "anticipate",
|
|
1031
|
+
back: "back",
|
|
1032
|
+
inBack: "inBack",
|
|
1033
|
+
outBack: "outBack",
|
|
1034
|
+
inOutBack: "inOutBack",
|
|
1035
|
+
inElastic: "inElastic",
|
|
1036
|
+
outElastic: "outElastic",
|
|
1037
|
+
inOutElastic: "inOutElastic",
|
|
1038
|
+
inBounce: "inBounce",
|
|
1039
|
+
outBounce: "outBounce",
|
|
1040
|
+
inOutBounce: "inOutBounce",
|
|
1041
|
+
cubicBezier(x1, y1, x2, y2) {
|
|
1042
|
+
return `cubic-bezier(${x1},${y1},${x2},${y2})`;
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
export const timing = {
|
|
1046
|
+
tween(options = {}) {
|
|
1047
|
+
assertOptionalPositiveInteger(options.durationFrames, "tween durationFrames");
|
|
1048
|
+
return compactObject({
|
|
1049
|
+
type: "tween",
|
|
1050
|
+
durationFrames: options.durationFrames,
|
|
1051
|
+
easing: options.easing
|
|
1052
|
+
});
|
|
1053
|
+
},
|
|
1054
|
+
spring(options = {}) {
|
|
1055
|
+
assertOptionalPositiveInteger(options.durationFrames, "spring durationFrames");
|
|
1056
|
+
assertOptionalPositiveNumber(options.stiffness, "spring stiffness");
|
|
1057
|
+
assertOptionalPositiveNumber(options.damping, "spring damping");
|
|
1058
|
+
assertOptionalPositiveNumber(options.mass, "spring mass");
|
|
1059
|
+
assertOptionalNonNegativeNumber(options.restSpeed, "spring restSpeed");
|
|
1060
|
+
assertOptionalRange(options.bounce, "spring bounce", 0, 1);
|
|
1061
|
+
return compactObject({
|
|
1062
|
+
type: "spring",
|
|
1063
|
+
durationFrames: options.durationFrames,
|
|
1064
|
+
stiffness: options.stiffness,
|
|
1065
|
+
damping: options.damping,
|
|
1066
|
+
mass: options.mass,
|
|
1067
|
+
restSpeed: options.restSpeed,
|
|
1068
|
+
bounce: options.bounce
|
|
1069
|
+
});
|
|
1070
|
+
},
|
|
1071
|
+
steps(options) {
|
|
1072
|
+
assertOptionalPositiveInteger(options.durationFrames, "steps durationFrames");
|
|
1073
|
+
assertPositiveInteger(options.steps, "steps");
|
|
1074
|
+
return compactObject({
|
|
1075
|
+
type: "steps",
|
|
1076
|
+
durationFrames: options.durationFrames,
|
|
1077
|
+
steps: options.steps,
|
|
1078
|
+
direction: options.direction
|
|
1079
|
+
});
|
|
1080
|
+
},
|
|
1081
|
+
sequence(segments) {
|
|
1082
|
+
if (segments.length === 0) {
|
|
1083
|
+
throw new Error("sequence timing requires at least one segment.");
|
|
1084
|
+
}
|
|
1085
|
+
return {
|
|
1086
|
+
type: "sequence",
|
|
1087
|
+
segments: segments.map((segment) => {
|
|
1088
|
+
assertPositiveInteger(segment.durationFrames, "sequence segment durationFrames");
|
|
1089
|
+
return compactObject({
|
|
1090
|
+
durationFrames: segment.durationFrames,
|
|
1091
|
+
timing: segment.timing,
|
|
1092
|
+
from: segment.from,
|
|
1093
|
+
to: segment.to
|
|
1094
|
+
});
|
|
1095
|
+
})
|
|
1096
|
+
};
|
|
1097
|
+
},
|
|
1098
|
+
stagger(options) {
|
|
1099
|
+
assertPositiveInteger(options.childCount, "stagger childCount");
|
|
1100
|
+
assertNonNegativeInteger(options.eachFrames, "stagger eachFrames");
|
|
1101
|
+
assertOptionalNonNegativeInteger(options.childIndex, "stagger childIndex");
|
|
1102
|
+
if (options.childIndex !== undefined && options.childIndex >= options.childCount) {
|
|
1103
|
+
throw new Error("stagger childIndex must be lower than childCount.");
|
|
1104
|
+
}
|
|
1105
|
+
return compactObject({
|
|
1106
|
+
type: "stagger",
|
|
1107
|
+
timing: options.timing,
|
|
1108
|
+
childCount: options.childCount,
|
|
1109
|
+
eachFrames: options.eachFrames,
|
|
1110
|
+
childIndex: options.childIndex,
|
|
1111
|
+
from: options.from
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
export const exportPreset = {
|
|
1116
|
+
vertical(options = {}) {
|
|
1117
|
+
return exportDefinition({
|
|
1118
|
+
name: "vertical-9x16",
|
|
1119
|
+
format: "mp4",
|
|
1120
|
+
codec: "h264",
|
|
1121
|
+
width: 1080,
|
|
1122
|
+
height: 1920,
|
|
1123
|
+
...options
|
|
1124
|
+
});
|
|
1125
|
+
},
|
|
1126
|
+
reels(options = {}) {
|
|
1127
|
+
return exportDefinition({
|
|
1128
|
+
name: "reels",
|
|
1129
|
+
format: "mp4",
|
|
1130
|
+
codec: "h264",
|
|
1131
|
+
width: 1080,
|
|
1132
|
+
height: 1920,
|
|
1133
|
+
...options
|
|
1134
|
+
});
|
|
1135
|
+
},
|
|
1136
|
+
instagramReels(options = {}) {
|
|
1137
|
+
return exportDefinition({
|
|
1138
|
+
name: "instagram-reels-9x16",
|
|
1139
|
+
format: "mp4",
|
|
1140
|
+
codec: "h264",
|
|
1141
|
+
width: 1080,
|
|
1142
|
+
height: 1920,
|
|
1143
|
+
...options
|
|
1144
|
+
});
|
|
1145
|
+
},
|
|
1146
|
+
tiktok(options = {}) {
|
|
1147
|
+
return exportDefinition({
|
|
1148
|
+
name: "tiktok-9x16",
|
|
1149
|
+
format: "mp4",
|
|
1150
|
+
codec: "h264",
|
|
1151
|
+
width: 1080,
|
|
1152
|
+
height: 1920,
|
|
1153
|
+
...options
|
|
1154
|
+
});
|
|
1155
|
+
},
|
|
1156
|
+
youtubeShorts(options = {}) {
|
|
1157
|
+
return exportDefinition({
|
|
1158
|
+
name: "youtube-shorts-9x16",
|
|
1159
|
+
format: "mp4",
|
|
1160
|
+
codec: "h264",
|
|
1161
|
+
width: 1080,
|
|
1162
|
+
height: 1920,
|
|
1163
|
+
...options
|
|
1164
|
+
});
|
|
1165
|
+
},
|
|
1166
|
+
facebookReels(options = {}) {
|
|
1167
|
+
return exportDefinition({
|
|
1168
|
+
name: "facebook-reels-9x16",
|
|
1169
|
+
format: "mp4",
|
|
1170
|
+
codec: "h264",
|
|
1171
|
+
width: 1080,
|
|
1172
|
+
height: 1920,
|
|
1173
|
+
...options
|
|
1174
|
+
});
|
|
1175
|
+
},
|
|
1176
|
+
square(options = {}) {
|
|
1177
|
+
return exportDefinition({
|
|
1178
|
+
name: "square",
|
|
1179
|
+
format: "mp4",
|
|
1180
|
+
codec: "h264",
|
|
1181
|
+
width: 1080,
|
|
1182
|
+
height: 1080,
|
|
1183
|
+
...options
|
|
1184
|
+
});
|
|
1185
|
+
},
|
|
1186
|
+
portrait(options = {}) {
|
|
1187
|
+
return exportDefinition({
|
|
1188
|
+
name: "portrait-4x5",
|
|
1189
|
+
format: "mp4",
|
|
1190
|
+
codec: "h264",
|
|
1191
|
+
width: 1080,
|
|
1192
|
+
height: 1350,
|
|
1193
|
+
...options
|
|
1194
|
+
});
|
|
1195
|
+
},
|
|
1196
|
+
landscape(options = {}) {
|
|
1197
|
+
return exportDefinition({
|
|
1198
|
+
name: "landscape",
|
|
1199
|
+
format: "mp4",
|
|
1200
|
+
codec: "h264",
|
|
1201
|
+
width: 1920,
|
|
1202
|
+
height: 1080,
|
|
1203
|
+
...options
|
|
1204
|
+
});
|
|
1205
|
+
},
|
|
1206
|
+
social(options = {}) {
|
|
1207
|
+
return compactArray([
|
|
1208
|
+
options.instagramReels === false ? undefined : exportPreset.instagramReels(options.instagramReels ?? {}),
|
|
1209
|
+
options.tiktok === false ? undefined : exportPreset.tiktok(options.tiktok ?? {}),
|
|
1210
|
+
options.youtubeShorts === false ? undefined : exportPreset.youtubeShorts(options.youtubeShorts ?? {}),
|
|
1211
|
+
options.facebookReels === false ? undefined : exportPreset.facebookReels(options.facebookReels ?? {}),
|
|
1212
|
+
options.reels === undefined || options.reels === false
|
|
1213
|
+
? undefined
|
|
1214
|
+
: exportPreset.reels({ name: "reels-9x16", ...options.reels }),
|
|
1215
|
+
options.square === false ? undefined : exportPreset.square({ name: "square-1x1", ...(options.square ?? {}) }),
|
|
1216
|
+
options.portrait === false ? undefined : exportPreset.portrait(options.portrait ?? {}),
|
|
1217
|
+
options.landscape === false
|
|
1218
|
+
? undefined
|
|
1219
|
+
: exportPreset.landscape({ name: "landscape-16x9", ...(options.landscape ?? {}) })
|
|
1220
|
+
]);
|
|
1221
|
+
},
|
|
1222
|
+
custom(options) {
|
|
1223
|
+
return exportDefinition({
|
|
1224
|
+
format: "mp4",
|
|
1225
|
+
codec: "h264",
|
|
1226
|
+
...options
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
export const exportPresets = exportPreset;
|
|
1231
|
+
export const vertical = exportPreset.vertical;
|
|
1232
|
+
export const reels = exportPreset.reels;
|
|
1233
|
+
export const instagramReels = exportPreset.instagramReels;
|
|
1234
|
+
export const tiktok = exportPreset.tiktok;
|
|
1235
|
+
export const youtubeShorts = exportPreset.youtubeShorts;
|
|
1236
|
+
export const facebookReels = exportPreset.facebookReels;
|
|
1237
|
+
export const square = exportPreset.square;
|
|
1238
|
+
export const portrait = exportPreset.portrait;
|
|
1239
|
+
export const landscape = exportPreset.landscape;
|
|
1240
|
+
export const social = exportPreset.social;
|
|
1241
|
+
export const customExport = exportPreset.custom;
|
|
1242
|
+
export const socialMediaPresets = [
|
|
1243
|
+
socialPresetDefinition("instagram-reels", "Instagram Reels", "Instagram", "9:16", exportPreset.instagramReels()),
|
|
1244
|
+
socialPresetDefinition("tiktok", "TikTok", "TikTok", "9:16", exportPreset.tiktok()),
|
|
1245
|
+
socialPresetDefinition("youtube-shorts", "YouTube Shorts", "YouTube", "9:16", exportPreset.youtubeShorts()),
|
|
1246
|
+
socialPresetDefinition("facebook-reels", "Facebook Reels", "Facebook", "9:16", exportPreset.facebookReels()),
|
|
1247
|
+
socialPresetDefinition("instagram-feed-portrait", "Instagram Feed Portrait", "Instagram", "4:5", exportPreset.portrait()),
|
|
1248
|
+
socialPresetDefinition("square-feed", "Square Feed", "Generic social", "1:1", exportPreset.square({ name: "square-1x1" })),
|
|
1249
|
+
socialPresetDefinition("landscape-feed", "Landscape Feed", "Generic social", "16:9", exportPreset.landscape({ name: "landscape-16x9" }))
|
|
1250
|
+
];
|
|
1251
|
+
export const crop = {
|
|
1252
|
+
center() {
|
|
1253
|
+
return { mode: "center" };
|
|
1254
|
+
},
|
|
1255
|
+
subject(options) {
|
|
1256
|
+
return compactObject({
|
|
1257
|
+
mode: "subject",
|
|
1258
|
+
x: options.x,
|
|
1259
|
+
y: options.y,
|
|
1260
|
+
keyframes: options.keyframes === undefined ? undefined : options.keyframes.map((frame) => ({ ...frame })),
|
|
1261
|
+
smoothingFrames: options.smoothingFrames,
|
|
1262
|
+
source: options.source
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
function buildLayer(id, type, options) {
|
|
1267
|
+
assertId(id, "layer");
|
|
1268
|
+
const { x, y, width, height, ...rest } = options;
|
|
1269
|
+
const layer = compactObject({
|
|
1270
|
+
id,
|
|
1271
|
+
type,
|
|
1272
|
+
...rest
|
|
1273
|
+
});
|
|
1274
|
+
if (layer.position === undefined && (x !== undefined || y !== undefined)) {
|
|
1275
|
+
layer.position = compactObject({ x, y });
|
|
1276
|
+
}
|
|
1277
|
+
if (layer.size === undefined && (width !== undefined || height !== undefined)) {
|
|
1278
|
+
layer.size = compactObject({ width, height });
|
|
1279
|
+
}
|
|
1280
|
+
return new LayerBuilder(layer);
|
|
1281
|
+
}
|
|
1282
|
+
function transitionDefinition(type, options) {
|
|
1283
|
+
return compactObject({
|
|
1284
|
+
type,
|
|
1285
|
+
durationFrames: options.durationFrames,
|
|
1286
|
+
direction: options.direction,
|
|
1287
|
+
axis: options.axis,
|
|
1288
|
+
shape: options.shape,
|
|
1289
|
+
color: options.color,
|
|
1290
|
+
amount: options.amount,
|
|
1291
|
+
intensity: options.intensity,
|
|
1292
|
+
easing: options.easing,
|
|
1293
|
+
timing: options.timing
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
function transitionSeriesDefinition(definition) {
|
|
1297
|
+
return {
|
|
1298
|
+
presentation: compactObject({
|
|
1299
|
+
type: definition.type,
|
|
1300
|
+
direction: definition.direction,
|
|
1301
|
+
axis: definition.axis,
|
|
1302
|
+
shape: definition.shape,
|
|
1303
|
+
color: definition.color,
|
|
1304
|
+
amount: definition.amount,
|
|
1305
|
+
intensity: definition.intensity,
|
|
1306
|
+
rows: definition.rows,
|
|
1307
|
+
columns: definition.columns
|
|
1308
|
+
}),
|
|
1309
|
+
timing: compactObject({
|
|
1310
|
+
type: "tween",
|
|
1311
|
+
durationFrames: definition.durationFrames,
|
|
1312
|
+
easing: definition.easing
|
|
1313
|
+
})
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
function entranceOpacityKeyframes(durationFrames, easingValue) {
|
|
1317
|
+
if (durationFrames <= 1) {
|
|
1318
|
+
return [{ frame: 0, value: 1 }];
|
|
1319
|
+
}
|
|
1320
|
+
return keyframes([
|
|
1321
|
+
[0, 0, easingValue],
|
|
1322
|
+
[durationFrames - 1, 1]
|
|
1323
|
+
]);
|
|
1324
|
+
}
|
|
1325
|
+
function zoomMotion(options, fallbackFromScale, fallbackToScale) {
|
|
1326
|
+
return {
|
|
1327
|
+
scale: cameraTrack(options.durationFrames, options.fromScale ?? fallbackFromScale, options.toScale ?? fallbackToScale, options.easing)
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
function cameraTrack(durationFrames, fromValue, toValue, easing) {
|
|
1331
|
+
const endFrame = cameraEndFrame(durationFrames);
|
|
1332
|
+
if (endFrame === 0) {
|
|
1333
|
+
return [{ frame: 0, value: fromValue }];
|
|
1334
|
+
}
|
|
1335
|
+
const firstFrame = compactObject({
|
|
1336
|
+
frame: 0,
|
|
1337
|
+
value: fromValue,
|
|
1338
|
+
easing
|
|
1339
|
+
});
|
|
1340
|
+
return [firstFrame, { frame: endFrame, value: toValue }];
|
|
1341
|
+
}
|
|
1342
|
+
function cameraThreePointTrack(durationFrames, fromValue, middleValue, toValue, easing) {
|
|
1343
|
+
const endFrame = cameraEndFrame(durationFrames);
|
|
1344
|
+
if (endFrame === 0) {
|
|
1345
|
+
return [{ frame: 0, value: fromValue }];
|
|
1346
|
+
}
|
|
1347
|
+
const middleFrame = Math.floor(endFrame / 2);
|
|
1348
|
+
if (middleFrame === 0 || middleFrame === endFrame) {
|
|
1349
|
+
return cameraTrack(durationFrames, fromValue, toValue, easing);
|
|
1350
|
+
}
|
|
1351
|
+
return [
|
|
1352
|
+
compactObject({ frame: 0, value: fromValue, easing }),
|
|
1353
|
+
compactObject({ frame: middleFrame, value: middleValue, easing }),
|
|
1354
|
+
{ frame: endFrame, value: toValue }
|
|
1355
|
+
];
|
|
1356
|
+
}
|
|
1357
|
+
function cameraCrashZoomTrack(options) {
|
|
1358
|
+
const endFrame = cameraEndFrame(options.durationFrames);
|
|
1359
|
+
if (endFrame === 0) {
|
|
1360
|
+
return [{ frame: 0, value: options.fromScale }];
|
|
1361
|
+
}
|
|
1362
|
+
const fallbackImpactFrame = Math.max(1, Math.floor(endFrame * 0.35));
|
|
1363
|
+
const impactFrame = clampInteger(options.impactFrame ?? fallbackImpactFrame, 1, endFrame);
|
|
1364
|
+
if (impactFrame === endFrame) {
|
|
1365
|
+
return keyframes([
|
|
1366
|
+
[0, options.fromScale, options.easing],
|
|
1367
|
+
[endFrame, options.toScale]
|
|
1368
|
+
]);
|
|
1369
|
+
}
|
|
1370
|
+
return keyframes([
|
|
1371
|
+
[0, options.fromScale, options.easing],
|
|
1372
|
+
[impactFrame, options.overshootScale, "outBack"],
|
|
1373
|
+
[endFrame, options.toScale]
|
|
1374
|
+
]);
|
|
1375
|
+
}
|
|
1376
|
+
function cameraJitterTrack(options) {
|
|
1377
|
+
const endFrame = cameraEndFrame(options.durationFrames);
|
|
1378
|
+
const intervalFrames = clampInteger(options.intervalFrames, 1, Math.max(1, endFrame));
|
|
1379
|
+
const frames = [];
|
|
1380
|
+
let state = normalizeSeed(options.seed);
|
|
1381
|
+
for (let frame = 0; frame <= endFrame; frame += intervalFrames) {
|
|
1382
|
+
state = nextSeed(state);
|
|
1383
|
+
const value = roundMotionValue(options.restingValue + (seedToUnit(state) * 2 - 1) * options.amount);
|
|
1384
|
+
frames.push(compactObject({
|
|
1385
|
+
frame,
|
|
1386
|
+
value,
|
|
1387
|
+
easing: frame === endFrame ? undefined : options.easing
|
|
1388
|
+
}));
|
|
1389
|
+
}
|
|
1390
|
+
if (frames[frames.length - 1]?.frame !== endFrame) {
|
|
1391
|
+
state = nextSeed(state);
|
|
1392
|
+
frames.push({
|
|
1393
|
+
frame: endFrame,
|
|
1394
|
+
value: roundMotionValue(options.restingValue + (seedToUnit(state) * 2 - 1) * options.amount)
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
if (frames.length === 1) {
|
|
1398
|
+
frames[0] = { frame: 0, value: options.restingValue };
|
|
1399
|
+
}
|
|
1400
|
+
return frames;
|
|
1401
|
+
}
|
|
1402
|
+
function cameraAxisFrames(options) {
|
|
1403
|
+
const halfAmount = options.amount / 2;
|
|
1404
|
+
const directionSign = options.direction === "left" || options.direction === "up" ? -1 : 1;
|
|
1405
|
+
const subjectBias = anchorBias(options.subjectAnchor, options.axis) * halfAmount * 0.5;
|
|
1406
|
+
const fromValue = options.fromValue ?? options.restingValue - directionSign * halfAmount + subjectBias;
|
|
1407
|
+
const toValue = options.toValue ?? options.restingValue + directionSign * halfAmount + subjectBias;
|
|
1408
|
+
return cameraTrack(options.durationFrames, fromValue, toValue, options.easing);
|
|
1409
|
+
}
|
|
1410
|
+
function cameraIntensity(options, fallback) {
|
|
1411
|
+
return Math.max(0, options.intensity ?? fallback);
|
|
1412
|
+
}
|
|
1413
|
+
function cameraTravelAmount(options, fallback) {
|
|
1414
|
+
const baseAmount = options.amount ?? fallback * (1 + cameraIntensity(options, 0));
|
|
1415
|
+
return baseAmount * safeAreaMultiplier(options.safeArea);
|
|
1416
|
+
}
|
|
1417
|
+
function cameraEndFrame(durationFrames) {
|
|
1418
|
+
if (!Number.isInteger(durationFrames) || durationFrames < 1) {
|
|
1419
|
+
throw new Error("Camera motion durationFrames must be a positive integer.");
|
|
1420
|
+
}
|
|
1421
|
+
return durationFrames - 1;
|
|
1422
|
+
}
|
|
1423
|
+
function safeAreaMultiplier(safeArea) {
|
|
1424
|
+
if (safeArea === undefined) {
|
|
1425
|
+
return 1;
|
|
1426
|
+
}
|
|
1427
|
+
if (typeof safeArea === "number") {
|
|
1428
|
+
return 1 - clamp(safeArea, 0, 0.45) * 2;
|
|
1429
|
+
}
|
|
1430
|
+
const largestInset = Math.max(safeArea.top ?? 0, safeArea.right ?? 0, safeArea.bottom ?? 0, safeArea.left ?? 0);
|
|
1431
|
+
return 1 - clamp(largestInset, 0, 0.45) * 2;
|
|
1432
|
+
}
|
|
1433
|
+
function anchorBias(anchor, axis) {
|
|
1434
|
+
if (anchor === undefined) {
|
|
1435
|
+
return 0;
|
|
1436
|
+
}
|
|
1437
|
+
if (typeof anchor === "object") {
|
|
1438
|
+
const value = axis === "x" ? anchor.x : anchor.y;
|
|
1439
|
+
return clamp(value, 0, 1) * 2 - 1;
|
|
1440
|
+
}
|
|
1441
|
+
if (axis === "x") {
|
|
1442
|
+
if (anchor.endsWith("left")) {
|
|
1443
|
+
return -1;
|
|
1444
|
+
}
|
|
1445
|
+
if (anchor.endsWith("right")) {
|
|
1446
|
+
return 1;
|
|
1447
|
+
}
|
|
1448
|
+
return 0;
|
|
1449
|
+
}
|
|
1450
|
+
if (anchor.startsWith("top")) {
|
|
1451
|
+
return -1;
|
|
1452
|
+
}
|
|
1453
|
+
if (anchor.startsWith("bottom")) {
|
|
1454
|
+
return 1;
|
|
1455
|
+
}
|
|
1456
|
+
return 0;
|
|
1457
|
+
}
|
|
1458
|
+
function clamp(value, min, max) {
|
|
1459
|
+
return Math.min(max, Math.max(min, value));
|
|
1460
|
+
}
|
|
1461
|
+
function clampInteger(value, min, max) {
|
|
1462
|
+
return Math.min(max, Math.max(min, Math.round(value)));
|
|
1463
|
+
}
|
|
1464
|
+
function normalizeSeed(seed) {
|
|
1465
|
+
const normalized = Math.trunc(seed) % 2147483647;
|
|
1466
|
+
return normalized <= 0 ? normalized + 2147483646 : normalized;
|
|
1467
|
+
}
|
|
1468
|
+
function nextSeed(seed) {
|
|
1469
|
+
return (seed * 16807) % 2147483647;
|
|
1470
|
+
}
|
|
1471
|
+
function seedToUnit(seed) {
|
|
1472
|
+
return (seed - 1) / 2147483646;
|
|
1473
|
+
}
|
|
1474
|
+
function roundMotionValue(value) {
|
|
1475
|
+
return Math.round(value * 1000) / 1000;
|
|
1476
|
+
}
|
|
1477
|
+
function exportDefinition(definition) {
|
|
1478
|
+
return compactObject(definition);
|
|
1479
|
+
}
|
|
1480
|
+
function socialPresetDefinition(id, label, platform, aspectRatio, preset) {
|
|
1481
|
+
const definition = {
|
|
1482
|
+
id,
|
|
1483
|
+
label,
|
|
1484
|
+
platform,
|
|
1485
|
+
aspectRatio,
|
|
1486
|
+
width: preset.width ?? 0,
|
|
1487
|
+
height: preset.height ?? 0,
|
|
1488
|
+
defaultName: preset.name ?? id,
|
|
1489
|
+
preset
|
|
1490
|
+
};
|
|
1491
|
+
if (preset.fps !== undefined) {
|
|
1492
|
+
definition.fps = preset.fps;
|
|
1493
|
+
}
|
|
1494
|
+
return definition;
|
|
1495
|
+
}
|
|
1496
|
+
function isKeyframeTuple(frame) {
|
|
1497
|
+
return Array.isArray(frame);
|
|
1498
|
+
}
|
|
1499
|
+
function interpolationFor(name) {
|
|
1500
|
+
return `{{${name}}}`;
|
|
1501
|
+
}
|
|
1502
|
+
function assertId(id, kind) {
|
|
1503
|
+
if (id.trim() === "") {
|
|
1504
|
+
throw new Error(`Kavio ${kind} id must not be empty.`);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
function assertPositiveInteger(value, name) {
|
|
1508
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
1509
|
+
throw new Error(`${name} must be a positive integer.`);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
function assertOptionalPositiveInteger(value, name) {
|
|
1513
|
+
if (value !== undefined) {
|
|
1514
|
+
assertPositiveInteger(value, name);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
function assertNonNegativeInteger(value, name) {
|
|
1518
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
1519
|
+
throw new Error(`${name} must be a non-negative integer.`);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
function assertOptionalNonNegativeInteger(value, name) {
|
|
1523
|
+
if (value !== undefined) {
|
|
1524
|
+
assertNonNegativeInteger(value, name);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
function assertOptionalPositiveNumber(value, name) {
|
|
1528
|
+
if (value !== undefined && (!Number.isFinite(value) || value <= 0)) {
|
|
1529
|
+
throw new Error(`${name} must be a positive number.`);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function assertOptionalNonNegativeNumber(value, name) {
|
|
1533
|
+
if (value !== undefined && (!Number.isFinite(value) || value < 0)) {
|
|
1534
|
+
throw new Error(`${name} must be a non-negative number.`);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
function assertOptionalRange(value, name, min, max) {
|
|
1538
|
+
if (value !== undefined && (!Number.isFinite(value) || value < min || value > max)) {
|
|
1539
|
+
throw new Error(`${name} must be between ${min} and ${max}.`);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function compactObject(input) {
|
|
1543
|
+
const output = {};
|
|
1544
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1545
|
+
if (value !== undefined) {
|
|
1546
|
+
output[key] = value;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return output;
|
|
1550
|
+
}
|
|
1551
|
+
function compactArray(input) {
|
|
1552
|
+
return input.filter((item) => item !== undefined);
|
|
1553
|
+
}
|
|
1554
|
+
function layerIdFor(value) {
|
|
1555
|
+
return value instanceof LayerBuilder ? value.id : value;
|
|
1556
|
+
}
|
|
1557
|
+
function cloneTrack(track) {
|
|
1558
|
+
return {
|
|
1559
|
+
id: track.id,
|
|
1560
|
+
clips: track.clips.map((clip) => cloneTrackClip(clip))
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
function cloneTrackClip(clip) {
|
|
1564
|
+
const output = {
|
|
1565
|
+
id: clip.id,
|
|
1566
|
+
layerId: clip.layerId,
|
|
1567
|
+
startFrame: clip.startFrame,
|
|
1568
|
+
durationFrames: clip.durationFrames
|
|
1569
|
+
};
|
|
1570
|
+
if (clip.transitionFromPrevious !== undefined) {
|
|
1571
|
+
output.transitionFromPrevious = cloneTransitionSeriesDefinition(clip.transitionFromPrevious);
|
|
1572
|
+
}
|
|
1573
|
+
return output;
|
|
1574
|
+
}
|
|
1575
|
+
function cloneTransitionSeriesDefinition(definition) {
|
|
1576
|
+
const output = {
|
|
1577
|
+
presentation: {
|
|
1578
|
+
type: definition.presentation.type
|
|
1579
|
+
},
|
|
1580
|
+
timing: {
|
|
1581
|
+
type: "tween",
|
|
1582
|
+
durationFrames: definition.timing.durationFrames
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
if (definition.presentation.direction !== undefined) {
|
|
1586
|
+
output.presentation.direction = definition.presentation.direction;
|
|
1587
|
+
}
|
|
1588
|
+
if (definition.presentation.axis !== undefined) {
|
|
1589
|
+
output.presentation.axis = definition.presentation.axis;
|
|
1590
|
+
}
|
|
1591
|
+
if (definition.presentation.shape !== undefined) {
|
|
1592
|
+
output.presentation.shape = definition.presentation.shape;
|
|
1593
|
+
}
|
|
1594
|
+
if (definition.presentation.color !== undefined) {
|
|
1595
|
+
output.presentation.color = definition.presentation.color;
|
|
1596
|
+
}
|
|
1597
|
+
if (definition.presentation.amount !== undefined) {
|
|
1598
|
+
output.presentation.amount = definition.presentation.amount;
|
|
1599
|
+
}
|
|
1600
|
+
if (definition.presentation.intensity !== undefined) {
|
|
1601
|
+
output.presentation.intensity = definition.presentation.intensity;
|
|
1602
|
+
}
|
|
1603
|
+
if (definition.presentation.rows !== undefined) {
|
|
1604
|
+
output.presentation.rows = definition.presentation.rows;
|
|
1605
|
+
}
|
|
1606
|
+
if (definition.presentation.columns !== undefined) {
|
|
1607
|
+
output.presentation.columns = definition.presentation.columns;
|
|
1608
|
+
}
|
|
1609
|
+
if (definition.timing.easing !== undefined) {
|
|
1610
|
+
output.timing.easing = definition.timing.easing;
|
|
1611
|
+
}
|
|
1612
|
+
return output;
|
|
1613
|
+
}
|
|
1614
|
+
function isTransitionSeriesDefinition(value) {
|
|
1615
|
+
return isRecord(value) && isRecord(value.presentation) && isRecord(value.timing);
|
|
1616
|
+
}
|
|
1617
|
+
function cloneAuthorObject(input) {
|
|
1618
|
+
const output = {};
|
|
1619
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1620
|
+
if (value instanceof PropReference || value instanceof AssetReference || value === undefined) {
|
|
1621
|
+
output[key] = value;
|
|
1622
|
+
}
|
|
1623
|
+
else if (Array.isArray(value)) {
|
|
1624
|
+
output[key] = value.map((item) => cloneAuthorValue(item));
|
|
1625
|
+
}
|
|
1626
|
+
else if (isRecord(value)) {
|
|
1627
|
+
output[key] = cloneAuthorObject(value);
|
|
1628
|
+
}
|
|
1629
|
+
else {
|
|
1630
|
+
output[key] = value;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
return output;
|
|
1634
|
+
}
|
|
1635
|
+
function cloneAuthorValue(value) {
|
|
1636
|
+
if (value instanceof PropReference || value instanceof AssetReference || value === undefined) {
|
|
1637
|
+
return value;
|
|
1638
|
+
}
|
|
1639
|
+
if (Array.isArray(value)) {
|
|
1640
|
+
return value.map((item) => cloneAuthorValue(item));
|
|
1641
|
+
}
|
|
1642
|
+
if (isRecord(value)) {
|
|
1643
|
+
return cloneAuthorObject(value);
|
|
1644
|
+
}
|
|
1645
|
+
return value;
|
|
1646
|
+
}
|
|
1647
|
+
function normalizeStandalone(value) {
|
|
1648
|
+
if (value instanceof PropReference) {
|
|
1649
|
+
return interpolationFor(value.name);
|
|
1650
|
+
}
|
|
1651
|
+
if (value instanceof AssetReference) {
|
|
1652
|
+
return value.id;
|
|
1653
|
+
}
|
|
1654
|
+
if (Array.isArray(value)) {
|
|
1655
|
+
return value.map((item) => normalizeStandalone(item));
|
|
1656
|
+
}
|
|
1657
|
+
if (isRecord(value)) {
|
|
1658
|
+
const output = {};
|
|
1659
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
1660
|
+
if (nested !== undefined) {
|
|
1661
|
+
output[key] = normalizeStandalone(nested);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return output;
|
|
1665
|
+
}
|
|
1666
|
+
return value;
|
|
1667
|
+
}
|
|
1668
|
+
function isRecord(value) {
|
|
1669
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1670
|
+
}
|