@editframe/elements 0.15.0-beta.18 → 0.15.0-beta.20
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/dist/EF_FRAMEGEN.js +1 -0
- package/dist/elements/EFMedia.d.ts +7 -3
- package/dist/elements/EFMedia.js +42 -90
- package/dist/elements/EFTemporal.browsertest.d.ts +4 -3
- package/dist/elements/EFTemporal.d.ts +14 -11
- package/dist/elements/EFTemporal.js +63 -87
- package/dist/elements/EFTimegroup.d.ts +1 -1
- package/dist/elements/EFTimegroup.js +12 -107
- package/dist/elements/EFVideo.js +3 -1
- package/dist/elements/durationConverter.d.ts +8 -8
- package/dist/elements/durationConverter.js +2 -2
- package/dist/elements/updateAnimations.d.ts +9 -0
- package/dist/elements/updateAnimations.js +62 -0
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/gui/EFFilmstrip.js +7 -16
- package/dist/gui/EFFitScale.d.ts +27 -0
- package/dist/gui/EFFitScale.js +137 -0
- package/dist/gui/EFWorkbench.d.ts +1 -5
- package/dist/gui/EFWorkbench.js +7 -54
- package/dist/gui/TWMixin.css.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/style.css +0 -6
- package/package.json +2 -2
- package/src/elements/EFMedia.browsertest.ts +10 -10
- package/src/elements/EFMedia.ts +48 -118
- package/src/elements/EFTemporal.browsertest.ts +64 -31
- package/src/elements/EFTemporal.ts +99 -119
- package/src/elements/EFTimegroup.ts +12 -131
- package/src/elements/EFVideo.ts +3 -1
- package/src/elements/durationConverter.ts +9 -4
- package/src/elements/updateAnimations.ts +88 -0
- package/src/gui/ContextMixin.ts +0 -3
- package/src/gui/EFFilmstrip.ts +7 -16
- package/src/gui/EFFitScale.ts +151 -0
- package/src/gui/EFWorkbench.ts +13 -63
- package/types.json +1 -1
|
@@ -18,6 +18,8 @@ export declare class TemporalMixinInterface {
|
|
|
18
18
|
*/
|
|
19
19
|
get hasExplicitDuration(): boolean;
|
|
20
20
|
|
|
21
|
+
get sourceStartMs(): number;
|
|
22
|
+
|
|
21
23
|
/**
|
|
22
24
|
* Used to trim the start of the media.
|
|
23
25
|
*
|
|
@@ -27,7 +29,7 @@ export declare class TemporalMixinInterface {
|
|
|
27
29
|
*
|
|
28
30
|
* @domAttribute "trimstart"
|
|
29
31
|
*/
|
|
30
|
-
get trimStartMs(): number;
|
|
32
|
+
get trimStartMs(): number | undefined;
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
35
|
* Used to trim the end of the media.
|
|
@@ -40,10 +42,10 @@ export declare class TemporalMixinInterface {
|
|
|
40
42
|
*/
|
|
41
43
|
get trimEndMs(): number;
|
|
42
44
|
|
|
43
|
-
set trimStartMs(value: number);
|
|
44
|
-
set trimEndMs(value: number);
|
|
45
|
-
set trimstart(value: string);
|
|
46
|
-
set trimend(value: string);
|
|
45
|
+
set trimStartMs(value: number | undefined);
|
|
46
|
+
set trimEndMs(value: number | undefined);
|
|
47
|
+
set trimstart(value: string | undefined);
|
|
48
|
+
set trimend(value: string | undefined);
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
51
|
* The source in time of the element.
|
|
@@ -64,7 +66,7 @@ export declare class TemporalMixinInterface {
|
|
|
64
66
|
*
|
|
65
67
|
* @domAttribute "sourcein"
|
|
66
68
|
*/
|
|
67
|
-
get sourceInMs(): number;
|
|
69
|
+
get sourceInMs(): number | undefined;
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
* The source out time of the element.
|
|
@@ -87,18 +89,22 @@ export declare class TemporalMixinInterface {
|
|
|
87
89
|
*
|
|
88
90
|
* @domAttribute "sourceout"
|
|
89
91
|
*/
|
|
90
|
-
get sourceOutMs(): number;
|
|
92
|
+
get sourceOutMs(): number | undefined;
|
|
91
93
|
|
|
92
|
-
set sourceInMs(value: number);
|
|
93
|
-
set sourceOutMs(value: number);
|
|
94
|
-
set sourcein(value: string);
|
|
95
|
-
set sourceout(value: string);
|
|
94
|
+
set sourceInMs(value: number | undefined);
|
|
95
|
+
set sourceOutMs(value: number | undefined);
|
|
96
|
+
set sourcein(value: string | undefined);
|
|
97
|
+
set sourceout(value: string | undefined);
|
|
96
98
|
|
|
97
99
|
/**
|
|
98
100
|
* @domAttribute "duration"
|
|
99
101
|
*/
|
|
100
102
|
get durationMs(): number;
|
|
101
103
|
|
|
104
|
+
get explicitDurationMs(): number | undefined;
|
|
105
|
+
|
|
106
|
+
get intrinsicDurationMs(): number | undefined;
|
|
107
|
+
|
|
102
108
|
/**
|
|
103
109
|
* The start time of the element within its root timegroup in milliseconds.
|
|
104
110
|
*
|
|
@@ -338,109 +344,85 @@ export const EFTemporal = <T extends Constructor<LitElement>>(
|
|
|
338
344
|
}
|
|
339
345
|
}
|
|
340
346
|
|
|
341
|
-
private _trimStartMs = 0;
|
|
342
347
|
@property({
|
|
343
348
|
type: Number,
|
|
344
349
|
attribute: "trimstart",
|
|
345
350
|
converter: durationConverter,
|
|
346
351
|
})
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
return;
|
|
352
|
+
_trimStartMs: number | undefined = undefined;
|
|
353
|
+
|
|
354
|
+
get trimStartMs() {
|
|
355
|
+
if (this._trimStartMs === undefined) {
|
|
356
|
+
return undefined;
|
|
353
357
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
durationConverter.toAttribute(value / 1000),
|
|
358
|
+
return Math.min(
|
|
359
|
+
Math.max(this._trimStartMs, 0),
|
|
360
|
+
this.intrinsicDurationMs ?? 0,
|
|
358
361
|
);
|
|
359
362
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
} else {
|
|
364
|
-
this.removeAttribute("trimstart");
|
|
365
|
-
}
|
|
363
|
+
|
|
364
|
+
set trimStartMs(value: number | undefined) {
|
|
365
|
+
this._trimStartMs = value;
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
private _trimEndMs = 0;
|
|
369
368
|
@property({
|
|
370
369
|
type: Number,
|
|
371
370
|
attribute: "trimend",
|
|
372
371
|
converter: durationConverter,
|
|
373
372
|
})
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
return;
|
|
373
|
+
_trimEndMs: number | undefined = undefined;
|
|
374
|
+
|
|
375
|
+
get trimEndMs() {
|
|
376
|
+
if (this._trimEndMs === undefined) {
|
|
377
|
+
return undefined;
|
|
380
378
|
}
|
|
381
|
-
this._trimEndMs
|
|
382
|
-
this.setAttribute("trimend", durationConverter.toAttribute(value / 1000));
|
|
379
|
+
return Math.min(this._trimEndMs, this.intrinsicDurationMs ?? 0);
|
|
383
380
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
} else {
|
|
388
|
-
this.removeAttribute("trimend");
|
|
389
|
-
}
|
|
381
|
+
|
|
382
|
+
set trimEndMs(value: number | undefined) {
|
|
383
|
+
this._trimEndMs = value;
|
|
390
384
|
}
|
|
391
385
|
|
|
392
|
-
private _sourceInMs: number | undefined;
|
|
393
386
|
@property({
|
|
394
387
|
type: Number,
|
|
395
388
|
attribute: "sourcein",
|
|
396
389
|
converter: durationConverter,
|
|
397
|
-
reflect: true,
|
|
398
390
|
})
|
|
399
|
-
|
|
400
|
-
|
|
391
|
+
_sourceInMs: number | undefined = undefined;
|
|
392
|
+
|
|
393
|
+
get sourceInMs() {
|
|
394
|
+
if (this._sourceInMs === undefined) {
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
return Math.max(this._sourceInMs, 0);
|
|
401
398
|
}
|
|
399
|
+
|
|
402
400
|
set sourceInMs(value: number | undefined) {
|
|
403
401
|
this._sourceInMs = value;
|
|
404
|
-
value !== undefined
|
|
405
|
-
? this.setAttribute(
|
|
406
|
-
"sourcein",
|
|
407
|
-
durationConverter.toAttribute(value / 1000),
|
|
408
|
-
)
|
|
409
|
-
: this.removeAttribute("sourcein");
|
|
410
|
-
}
|
|
411
|
-
set sourcein(value: string | undefined) {
|
|
412
|
-
if (value !== undefined) {
|
|
413
|
-
this.setAttribute("sourcein", value);
|
|
414
|
-
} else {
|
|
415
|
-
this.removeAttribute("sourcein");
|
|
416
|
-
}
|
|
417
402
|
}
|
|
418
403
|
|
|
419
|
-
private _sourceOutMs: number | undefined;
|
|
420
404
|
@property({
|
|
421
405
|
type: Number,
|
|
422
406
|
attribute: "sourceout",
|
|
423
407
|
converter: durationConverter,
|
|
424
|
-
reflect: true,
|
|
425
408
|
})
|
|
426
|
-
|
|
427
|
-
|
|
409
|
+
_sourceOutMs: number | undefined = undefined;
|
|
410
|
+
|
|
411
|
+
get sourceOutMs() {
|
|
412
|
+
if (this._sourceOutMs === undefined) {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
if (
|
|
416
|
+
this.intrinsicDurationMs &&
|
|
417
|
+
this._sourceOutMs > this.intrinsicDurationMs
|
|
418
|
+
) {
|
|
419
|
+
return this.intrinsicDurationMs;
|
|
420
|
+
}
|
|
421
|
+
return Math.max(this._sourceOutMs, 0);
|
|
428
422
|
}
|
|
423
|
+
|
|
429
424
|
set sourceOutMs(value: number | undefined) {
|
|
430
425
|
this._sourceOutMs = value;
|
|
431
|
-
value !== undefined
|
|
432
|
-
? this.setAttribute(
|
|
433
|
-
"sourceout",
|
|
434
|
-
durationConverter.toAttribute(value / 1000),
|
|
435
|
-
)
|
|
436
|
-
: this.removeAttribute("sourceout");
|
|
437
|
-
}
|
|
438
|
-
set sourceout(value: string | undefined) {
|
|
439
|
-
if (value !== undefined) {
|
|
440
|
-
this.setAttribute("sourceout", value);
|
|
441
|
-
} else {
|
|
442
|
-
this.removeAttribute("sourceout");
|
|
443
|
-
}
|
|
444
426
|
}
|
|
445
427
|
|
|
446
428
|
@property({
|
|
@@ -469,28 +451,51 @@ export const EFTemporal = <T extends Constructor<LitElement>>(
|
|
|
469
451
|
return this._durationMs !== undefined;
|
|
470
452
|
}
|
|
471
453
|
|
|
454
|
+
get explicitDurationMs() {
|
|
455
|
+
if (this.hasExplicitDuration) {
|
|
456
|
+
return this._durationMs;
|
|
457
|
+
}
|
|
458
|
+
return undefined;
|
|
459
|
+
}
|
|
460
|
+
|
|
472
461
|
get hasOwnDuration() {
|
|
473
462
|
return false;
|
|
474
463
|
}
|
|
475
464
|
|
|
476
|
-
|
|
477
|
-
|
|
465
|
+
get intrinsicDurationMs() {
|
|
466
|
+
return undefined;
|
|
467
|
+
}
|
|
468
|
+
|
|
478
469
|
get durationMs() {
|
|
479
|
-
if (this.
|
|
480
|
-
return
|
|
481
|
-
this._durationMs ||
|
|
482
|
-
this.parentTimegroup?.durationMs ||
|
|
483
|
-
0 - this.sourceInMs
|
|
484
|
-
);
|
|
470
|
+
if (this.intrinsicDurationMs === undefined) {
|
|
471
|
+
return this._durationMs || this.parentTimegroup?.durationMs || 0;
|
|
485
472
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
this.
|
|
490
|
-
0 -
|
|
491
|
-
|
|
473
|
+
|
|
474
|
+
if (this.trimStartMs || this.trimEndMs) {
|
|
475
|
+
const trimmedDurationMs =
|
|
476
|
+
this.intrinsicDurationMs -
|
|
477
|
+
(this.trimStartMs ?? 0) -
|
|
478
|
+
(this.trimEndMs ?? 0);
|
|
479
|
+
if (trimmedDurationMs < 0) {
|
|
480
|
+
return 0;
|
|
481
|
+
}
|
|
482
|
+
return trimmedDurationMs;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (this.sourceInMs || this.sourceOutMs) {
|
|
486
|
+
const sourceInMs = this.sourceInMs ?? 0;
|
|
487
|
+
const sourceOutMs = this.sourceOutMs ?? this.intrinsicDurationMs;
|
|
488
|
+
if (sourceInMs >= sourceOutMs) {
|
|
489
|
+
return 0;
|
|
490
|
+
}
|
|
491
|
+
return sourceOutMs - sourceInMs;
|
|
492
492
|
}
|
|
493
|
-
|
|
493
|
+
|
|
494
|
+
return this.intrinsicDurationMs;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
get sourceStartMs() {
|
|
498
|
+
return this.trimStartMs ?? this.sourceInMs ?? 0;
|
|
494
499
|
}
|
|
495
500
|
|
|
496
501
|
get offsetMs() {
|
|
@@ -586,33 +591,8 @@ export const EFTemporal = <T extends Constructor<LitElement>>(
|
|
|
586
591
|
* for mapping to internal media time codes for audio/video elements.
|
|
587
592
|
*/
|
|
588
593
|
get currentSourceTimeMs() {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
return Math.min(
|
|
592
|
-
Math.max(
|
|
593
|
-
0,
|
|
594
|
-
this.rootTimegroup.currentTimeMs -
|
|
595
|
-
this.startTimeMs +
|
|
596
|
-
this.trimStartMs +
|
|
597
|
-
this.sourceInMs,
|
|
598
|
-
),
|
|
599
|
-
this.durationMs +
|
|
600
|
-
Math.abs(this.startOffsetMs) +
|
|
601
|
-
this.trimStartMs +
|
|
602
|
-
this.sourceInMs,
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
return Math.min(
|
|
606
|
-
Math.max(
|
|
607
|
-
0,
|
|
608
|
-
this.rootTimegroup.currentTimeMs -
|
|
609
|
-
this.startTimeMs +
|
|
610
|
-
this.trimStartMs,
|
|
611
|
-
),
|
|
612
|
-
this.durationMs + Math.abs(this.startOffsetMs) + this.trimStartMs,
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
return 0;
|
|
594
|
+
const leadingTrimMs = this.sourceInMs || this.trimStartMs || 0;
|
|
595
|
+
return this.ownCurrentTimeMs + leadingTrimMs;
|
|
616
596
|
}
|
|
617
597
|
|
|
618
598
|
frameTask = new Task(this, {
|
|
@@ -9,12 +9,12 @@ import { isContextMixin } from "../gui/ContextMixin.js";
|
|
|
9
9
|
import { deepGetMediaElements } from "./EFMedia.js";
|
|
10
10
|
import {
|
|
11
11
|
EFTemporal,
|
|
12
|
-
isEFTemporal,
|
|
13
12
|
shallowGetTemporalElements,
|
|
14
13
|
timegroupContext,
|
|
15
14
|
} from "./EFTemporal.js";
|
|
16
15
|
import { TimegroupController } from "./TimegroupController.js";
|
|
17
16
|
import { durationConverter } from "./durationConverter.js";
|
|
17
|
+
import { updateAnimations } from "./updateAnimations.ts";
|
|
18
18
|
|
|
19
19
|
const log = debug("ef:elements:EFTimegroup");
|
|
20
20
|
|
|
@@ -40,7 +40,8 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
40
40
|
width: 100%;
|
|
41
41
|
height: 100%;
|
|
42
42
|
position: absolute;
|
|
43
|
-
|
|
43
|
+
top: 0;
|
|
44
|
+
left: 0;
|
|
44
45
|
}
|
|
45
46
|
`;
|
|
46
47
|
|
|
@@ -121,14 +122,6 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
121
122
|
this.wrapWithWorkbench();
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
// // Create resize observer to handle scaling
|
|
125
|
-
// this.#resizeObserver = new ResizeObserver(() => this.updateScale());
|
|
126
|
-
// if (this.parentElement) {
|
|
127
|
-
// this.#resizeObserver.observe(this.parentElement);
|
|
128
|
-
// }
|
|
129
|
-
|
|
130
|
-
// Initialize animations when component is first connected
|
|
131
|
-
// Regrettably, this doesn't work without the requestAnimationFrame
|
|
132
125
|
requestAnimationFrame(() => {
|
|
133
126
|
this.updateAnimations();
|
|
134
127
|
});
|
|
@@ -139,53 +132,6 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
139
132
|
this.#resizeObserver?.disconnect();
|
|
140
133
|
}
|
|
141
134
|
|
|
142
|
-
// private get displayedParent(): Element | null {
|
|
143
|
-
// let displayedParent = this.parentElement;
|
|
144
|
-
// while (
|
|
145
|
-
// displayedParent &&
|
|
146
|
-
// getComputedStyle(displayedParent).display === "contents"
|
|
147
|
-
// ) {
|
|
148
|
-
// displayedParent = displayedParent.parentElement;
|
|
149
|
-
// }
|
|
150
|
-
// return displayedParent;
|
|
151
|
-
// }
|
|
152
|
-
|
|
153
|
-
// private updateScale() {
|
|
154
|
-
// if (this.fit === "none") return;
|
|
155
|
-
|
|
156
|
-
// const displayedParent = this.displayedParent;
|
|
157
|
-
// if (!displayedParent) return;
|
|
158
|
-
|
|
159
|
-
// // Get the natural size of the content
|
|
160
|
-
// const contentWidth = this.clientWidth;
|
|
161
|
-
// const contentHeight = this.clientHeight;
|
|
162
|
-
|
|
163
|
-
// // Get the available space from displayed parent
|
|
164
|
-
// const containerWidth = displayedParent.clientWidth;
|
|
165
|
-
// const containerHeight = displayedParent.clientHeight;
|
|
166
|
-
|
|
167
|
-
// // Calculate scale ratios
|
|
168
|
-
// const widthRatio = containerWidth / contentWidth;
|
|
169
|
-
// const heightRatio = containerHeight / contentHeight;
|
|
170
|
-
|
|
171
|
-
// let scale: number;
|
|
172
|
-
// if (this.fit === "contain") {
|
|
173
|
-
// // Use height ratio for contain mode to ensure it fits vertically
|
|
174
|
-
// scale = heightRatio;
|
|
175
|
-
|
|
176
|
-
// // If width would overflow after scaling, use width ratio instead
|
|
177
|
-
// if (contentWidth * scale > containerWidth) {
|
|
178
|
-
// scale = widthRatio;
|
|
179
|
-
// }
|
|
180
|
-
// } else {
|
|
181
|
-
// // cover
|
|
182
|
-
// scale = Math.max(widthRatio, heightRatio);
|
|
183
|
-
// }
|
|
184
|
-
|
|
185
|
-
// // Apply transform with fixed center origin
|
|
186
|
-
// this.style.transform = `scale(${scale})`;
|
|
187
|
-
// }
|
|
188
|
-
|
|
189
135
|
get storageKey() {
|
|
190
136
|
if (!this.id) {
|
|
191
137
|
throw new Error("Timegroup must have an id to use localStorage.");
|
|
@@ -193,6 +139,13 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
193
139
|
return `ef-timegroup-${this.id}`;
|
|
194
140
|
}
|
|
195
141
|
|
|
142
|
+
get intrinsicDurationMs() {
|
|
143
|
+
if (this.hasExplicitDuration) {
|
|
144
|
+
return this.explicitDurationMs;
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
196
149
|
get durationMs() {
|
|
197
150
|
switch (this.mode) {
|
|
198
151
|
case "fixed":
|
|
@@ -210,7 +163,7 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
210
163
|
case "contain": {
|
|
211
164
|
let maxDuration = 0;
|
|
212
165
|
for (const node of this.childTemporals) {
|
|
213
|
-
if (node.
|
|
166
|
+
if (node.intrinsicDurationMs !== undefined) {
|
|
214
167
|
maxDuration = Math.max(maxDuration, node.durationMs);
|
|
215
168
|
}
|
|
216
169
|
}
|
|
@@ -251,75 +204,7 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
251
204
|
}
|
|
252
205
|
|
|
253
206
|
private updateAnimations() {
|
|
254
|
-
this
|
|
255
|
-
"--ef-progress",
|
|
256
|
-
`${Math.max(0, Math.min(1, this.currentTimeMs / this.durationMs)) * 100}%`,
|
|
257
|
-
);
|
|
258
|
-
const timelineTimeMs = (this.rootTimegroup ?? this).currentTimeMs;
|
|
259
|
-
if (this.startTimeMs > timelineTimeMs || this.endTimeMs < timelineTimeMs) {
|
|
260
|
-
this.style.display = "none";
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
this.style.display = "";
|
|
264
|
-
const animations = this.getAnimations({ subtree: true });
|
|
265
|
-
this.style.setProperty("--ef-duration", `${this.durationMs}ms`);
|
|
266
|
-
this.style.setProperty(
|
|
267
|
-
"--ef-transition-duration",
|
|
268
|
-
`${this.parentTimegroup?.overlapMs ?? 0}ms`,
|
|
269
|
-
);
|
|
270
|
-
this.style.setProperty(
|
|
271
|
-
"--ef-transition-out-start",
|
|
272
|
-
`${this.durationMs - (this.parentTimegroup?.overlapMs ?? 0)}ms`,
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
for (const animation of animations) {
|
|
276
|
-
if (animation.playState === "running") {
|
|
277
|
-
animation.pause();
|
|
278
|
-
}
|
|
279
|
-
const effect = animation.effect;
|
|
280
|
-
if (!(effect && effect instanceof KeyframeEffect)) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
const target = effect.target;
|
|
284
|
-
// TODO: better generalize work avoidance for temporal elements
|
|
285
|
-
if (!target) {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
if (target.closest("ef-timegroup") !== this) {
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Important to avoid going to the end of the animation
|
|
293
|
-
// or it will reset awkwardly.
|
|
294
|
-
if (isEFTemporal(target)) {
|
|
295
|
-
const timing = effect.getTiming();
|
|
296
|
-
const duration = Number(timing.duration) ?? 0;
|
|
297
|
-
const delay = Number(timing.delay);
|
|
298
|
-
const newTime = Math.floor(
|
|
299
|
-
Math.min(target.ownCurrentTimeMs, duration - 1 + delay),
|
|
300
|
-
);
|
|
301
|
-
if (Number.isNaN(newTime)) {
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
animation.currentTime = newTime;
|
|
305
|
-
} else if (target) {
|
|
306
|
-
const nearestTimegroup = target.closest("ef-timegroup");
|
|
307
|
-
if (!nearestTimegroup) {
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const timing = effect.getTiming();
|
|
311
|
-
const duration = Number(timing.duration) ?? 0;
|
|
312
|
-
const delay = Number(timing.delay);
|
|
313
|
-
const newTime = Math.floor(
|
|
314
|
-
Math.min(nearestTimegroup.ownCurrentTimeMs, duration - 1 + delay),
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
if (Number.isNaN(newTime)) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
animation.currentTime = newTime;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
207
|
+
updateAnimations(this);
|
|
323
208
|
}
|
|
324
209
|
|
|
325
210
|
get contextProvider() {
|
|
@@ -367,10 +252,6 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
367
252
|
workbench.append(filmstrip);
|
|
368
253
|
}
|
|
369
254
|
|
|
370
|
-
get hasOwnDuration() {
|
|
371
|
-
return true;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
255
|
get efElements() {
|
|
375
256
|
return Array.from(
|
|
376
257
|
this.querySelectorAll(
|
package/src/elements/EFVideo.ts
CHANGED
|
@@ -20,7 +20,9 @@ export class EFVideo extends TWMixin(EFMedia) {
|
|
|
20
20
|
];
|
|
21
21
|
canvasRef = createRef<HTMLCanvasElement>();
|
|
22
22
|
render() {
|
|
23
|
-
return html`
|
|
23
|
+
return html`
|
|
24
|
+
<canvas ${ref(this.canvasRef)}></canvas>
|
|
25
|
+
`;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
get canvasElement() {
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { parseTimeToMs } from "./parseTimeToMs.js";
|
|
2
2
|
|
|
3
3
|
export const durationConverter = {
|
|
4
|
-
fromAttribute: (value: string)
|
|
5
|
-
|
|
4
|
+
fromAttribute: (value: string | null) =>
|
|
5
|
+
value === null ? null : parseTimeToMs(value),
|
|
6
|
+
toAttribute: (value: number | null) => (value === null ? null : `${value}s`),
|
|
6
7
|
};
|
|
7
8
|
|
|
8
9
|
const positiveDurationConverter = (error: string) => {
|
|
9
10
|
return {
|
|
10
|
-
fromAttribute: (value: string): number => {
|
|
11
|
+
fromAttribute: (value: string | null): number | null => {
|
|
12
|
+
if (value === null) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
11
15
|
if (value.startsWith("-")) {
|
|
12
16
|
throw new Error(error);
|
|
13
17
|
}
|
|
14
18
|
return parseTimeToMs(value);
|
|
15
19
|
},
|
|
16
|
-
toAttribute: (value: number) =>
|
|
20
|
+
toAttribute: (value: number | null) =>
|
|
21
|
+
value === null ? null : `${value}s`,
|
|
17
22
|
};
|
|
18
23
|
};
|
|
19
24
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { isEFTemporal } from "./EFTemporal.ts";
|
|
2
|
+
import type { EFTimegroup } from "./EFTimegroup.ts";
|
|
3
|
+
|
|
4
|
+
export const updateAnimations = (
|
|
5
|
+
element: HTMLElement & {
|
|
6
|
+
currentTimeMs: number;
|
|
7
|
+
durationMs: number;
|
|
8
|
+
rootTimegroup?: EFTimegroup;
|
|
9
|
+
parentTimegroup?: EFTimegroup;
|
|
10
|
+
startTimeMs: number;
|
|
11
|
+
endTimeMs: number;
|
|
12
|
+
},
|
|
13
|
+
) => {
|
|
14
|
+
element.style.setProperty(
|
|
15
|
+
"--ef-progress",
|
|
16
|
+
`${Math.max(0, Math.min(1, element.currentTimeMs / element.durationMs)) * 100}%`,
|
|
17
|
+
);
|
|
18
|
+
const timelineTimeMs = (element.rootTimegroup ?? element).currentTimeMs;
|
|
19
|
+
if (
|
|
20
|
+
element.startTimeMs > timelineTimeMs ||
|
|
21
|
+
element.endTimeMs < timelineTimeMs
|
|
22
|
+
) {
|
|
23
|
+
element.style.display = "none";
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
element.style.display = "";
|
|
27
|
+
const animations = element.getAnimations({ subtree: true });
|
|
28
|
+
element.style.setProperty("--ef-duration", `${element.durationMs}ms`);
|
|
29
|
+
element.style.setProperty(
|
|
30
|
+
"--ef-transition-duration",
|
|
31
|
+
`${element.parentTimegroup?.overlapMs ?? 0}ms`,
|
|
32
|
+
);
|
|
33
|
+
element.style.setProperty(
|
|
34
|
+
"--ef-transition-out-start",
|
|
35
|
+
`${element.durationMs - (element.parentTimegroup?.overlapMs ?? 0)}ms`,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
for (const animation of animations) {
|
|
39
|
+
if (animation.playState === "running") {
|
|
40
|
+
animation.pause();
|
|
41
|
+
}
|
|
42
|
+
const effect = animation.effect;
|
|
43
|
+
if (!(effect && effect instanceof KeyframeEffect)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const target = effect.target;
|
|
47
|
+
// TODO: better generalize work avoidance for temporal elements
|
|
48
|
+
if (!target) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (target.closest("ef-timegroup") !== element) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const timing = effect.getTiming();
|
|
56
|
+
const duration = Number(timing.duration) ?? 0;
|
|
57
|
+
const delay = Number(timing.delay) ?? 0;
|
|
58
|
+
const iterations = Number(timing.iterations) ?? 1;
|
|
59
|
+
|
|
60
|
+
const timeTarget = isEFTemporal(target)
|
|
61
|
+
? target
|
|
62
|
+
: target.closest("ef-timegroup");
|
|
63
|
+
if (!timeTarget) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const currentTime = timeTarget.ownCurrentTimeMs;
|
|
68
|
+
|
|
69
|
+
// Handle delay - don't start animation until delay is complete
|
|
70
|
+
if (currentTime < delay) {
|
|
71
|
+
animation.currentTime = 0;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const currentIteration = Math.floor((currentTime - delay) / duration);
|
|
76
|
+
const currentIterationTime = (currentTime - delay) % duration;
|
|
77
|
+
|
|
78
|
+
if (currentIteration >= iterations) {
|
|
79
|
+
// Stop just before the end to prevent DOM removal
|
|
80
|
+
animation.currentTime = duration - 0.01;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Ensure we never reach exactly duration
|
|
85
|
+
animation.currentTime =
|
|
86
|
+
Math.min(currentIterationTime, duration - 0.01) + delay;
|
|
87
|
+
}
|
|
88
|
+
};
|
package/src/gui/ContextMixin.ts
CHANGED
|
@@ -129,9 +129,6 @@ export function ContextMixin<T extends Constructor<LitElement>>(superClass: T) {
|
|
|
129
129
|
@property({ type: Boolean, reflect: true })
|
|
130
130
|
loop = false;
|
|
131
131
|
|
|
132
|
-
// @state()
|
|
133
|
-
// private stageScale = 1;
|
|
134
|
-
|
|
135
132
|
@property({ type: Boolean })
|
|
136
133
|
rendering = false;
|
|
137
134
|
|