@editframe/elements 0.15.0-beta.9 → 0.16.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EF_FRAMEGEN.d.ts +14 -10
- package/dist/EF_FRAMEGEN.js +17 -28
- package/dist/elements/EFCaptions.js +0 -7
- package/dist/elements/EFImage.js +0 -4
- package/dist/elements/EFMedia.d.ts +13 -8
- package/dist/elements/EFMedia.js +163 -146
- package/dist/elements/EFSourceMixin.js +2 -1
- 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 +2 -4
- package/dist/elements/EFTimegroup.js +15 -103
- package/dist/elements/EFVideo.js +3 -1
- package/dist/elements/EFWaveform.d.ts +1 -1
- package/dist/elements/EFWaveform.js +11 -28
- 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 +51 -0
- package/dist/getRenderInfo.js +72 -0
- package/dist/gui/EFFilmstrip.js +7 -16
- package/dist/gui/EFFitScale.d.ts +27 -0
- package/dist/gui/EFFitScale.js +138 -0
- package/dist/gui/EFWorkbench.d.ts +2 -5
- package/dist/gui/EFWorkbench.js +11 -56
- package/dist/gui/TWMixin.css.js +1 -1
- package/dist/gui/TWMixin.js +14 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -1
- package/dist/style.css +3 -3
- package/package.json +4 -3
- package/src/elements/EFCaptions.browsertest.ts +2 -2
- package/src/elements/EFCaptions.ts +0 -7
- package/src/elements/EFImage.browsertest.ts +2 -2
- package/src/elements/EFImage.ts +0 -4
- package/src/elements/EFMedia.browsertest.ts +14 -14
- package/src/elements/EFMedia.ts +219 -182
- package/src/elements/EFSourceMixin.ts +4 -4
- package/src/elements/EFTemporal.browsertest.ts +64 -31
- package/src/elements/EFTemporal.ts +99 -119
- package/src/elements/EFTimegroup.ts +15 -133
- package/src/elements/EFVideo.ts +3 -1
- package/src/elements/EFWaveform.ts +10 -44
- 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 +152 -0
- package/src/gui/EFWorkbench.ts +16 -65
- package/src/gui/TWMixin.ts +19 -2
- package/types.json +1 -1
|
@@ -3,76 +3,109 @@ import { customElement } from "lit/decorators/custom-element.js";
|
|
|
3
3
|
import { describe, expect, test } from "vitest";
|
|
4
4
|
import { EFTemporal } from "./EFTemporal.js";
|
|
5
5
|
|
|
6
|
-
@customElement("
|
|
7
|
-
class
|
|
6
|
+
@customElement("ten-seconds")
|
|
7
|
+
class TenSeconds extends EFTemporal(LitElement) {
|
|
8
|
+
get intrinsicDurationMs() {
|
|
9
|
+
return 10_000;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
8
12
|
|
|
9
13
|
declare global {
|
|
10
14
|
interface HTMLElementTagNameMap {
|
|
11
|
-
"
|
|
15
|
+
"ten-seconds": TenSeconds;
|
|
12
16
|
}
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
describe("sourcein and sourceout", () => {
|
|
16
20
|
test("sourcein and sourceout are parsed correctly", () => {
|
|
17
|
-
const element = document.createElement("
|
|
21
|
+
const element = document.createElement("ten-seconds");
|
|
18
22
|
element.setAttribute("sourcein", "1s");
|
|
19
23
|
element.setAttribute("sourceout", "5s");
|
|
20
24
|
expect(element.sourceInMs).toBe(1_000);
|
|
21
25
|
expect(element.sourceOutMs).toBe(5_000);
|
|
26
|
+
expect(element.durationMs).toBe(4_000);
|
|
22
27
|
});
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
describe("only srcin is set", () => {
|
|
30
|
+
test("duration is calculated", () => {
|
|
31
|
+
const element = document.createElement("ten-seconds");
|
|
32
|
+
element.sourceInMs = 1_000;
|
|
33
|
+
expect(element.durationMs).toBe(9_000);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("only srcout is set", () => {
|
|
38
|
+
test("duration is calculated", () => {
|
|
39
|
+
const element = document.createElement("ten-seconds");
|
|
40
|
+
element.sourceOutMs = 5_000;
|
|
41
|
+
expect(element.durationMs).toBe(5_000);
|
|
42
|
+
});
|
|
30
43
|
});
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
describe("srcout is before srcin", () => {
|
|
46
|
+
test("duration is zero", () => {
|
|
47
|
+
const element = document.createElement("ten-seconds");
|
|
48
|
+
element.sourceInMs = 5_000;
|
|
49
|
+
element.sourceOutMs = 1_000;
|
|
50
|
+
console.log(element.sourceInMs, element.sourceOutMs, element.durationMs);
|
|
51
|
+
expect(element.durationMs).toBe(0);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("srcin is negative", () => {
|
|
56
|
+
test("srcin is normalized to 0 ", () => {
|
|
57
|
+
const element = document.createElement("ten-seconds");
|
|
58
|
+
element.sourceInMs = -1_000;
|
|
59
|
+
expect(element.sourceInMs).toBe(0);
|
|
60
|
+
expect(element.durationMs).toBe(10_000);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("srcout is beyond the intrinsic duration", () => {
|
|
65
|
+
test("srcout is normalized to the intrinsic duration", () => {
|
|
66
|
+
const element = document.createElement("ten-seconds");
|
|
67
|
+
element.sourceOutMs = 15_000;
|
|
68
|
+
expect(element.sourceOutMs).toBe(10_000);
|
|
69
|
+
});
|
|
38
70
|
});
|
|
39
71
|
});
|
|
40
72
|
|
|
41
73
|
describe("trimstart and trimend", () => {
|
|
42
74
|
test("trimstart and trimend attributes are parsed correctly", () => {
|
|
43
|
-
const element = document.createElement("
|
|
75
|
+
const element = document.createElement("ten-seconds");
|
|
44
76
|
element.setAttribute("trimstart", "1s");
|
|
45
77
|
element.setAttribute("trimend", "5s");
|
|
46
78
|
expect(element.trimStartMs).toBe(1_000);
|
|
47
79
|
expect(element.trimEndMs).toBe(5_000);
|
|
80
|
+
expect(element.durationMs).toBe(4_000);
|
|
48
81
|
});
|
|
49
82
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
83
|
+
describe("trimstart is beyond the intrinsic duration", () => {
|
|
84
|
+
test("trimstart is normalized to the intrinsic duration", () => {
|
|
85
|
+
const element = document.createElement("ten-seconds");
|
|
86
|
+
element.trimStartMs = 15_000;
|
|
87
|
+
expect(element.trimStartMs).toBe(10_000);
|
|
88
|
+
});
|
|
56
89
|
});
|
|
57
90
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
91
|
+
describe("trimend is beyond the intrinsic duration", () => {
|
|
92
|
+
test("trimend is normalized to the intrinsic duration", () => {
|
|
93
|
+
const element = document.createElement("ten-seconds");
|
|
94
|
+
element.trimEndMs = 15_000;
|
|
95
|
+
expect(element.trimEndMs).toBe(10_000);
|
|
96
|
+
});
|
|
64
97
|
});
|
|
65
98
|
});
|
|
66
99
|
|
|
67
100
|
describe("duration", () => {
|
|
68
101
|
test("duration is parsed correctly", () => {
|
|
69
|
-
const element = document.createElement("
|
|
102
|
+
const element = document.createElement("ten-seconds");
|
|
70
103
|
element.setAttribute("duration", "10s");
|
|
71
104
|
expect(element.durationMs).toBe(10_000);
|
|
72
105
|
});
|
|
73
106
|
|
|
74
107
|
test("duration can be set directly on the element", () => {
|
|
75
|
-
const element = document.createElement("
|
|
108
|
+
const element = document.createElement("ten-seconds");
|
|
76
109
|
element.duration = "10s";
|
|
77
110
|
expect(element.durationMs).toBe(10_000);
|
|
78
111
|
});
|
|
@@ -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,16 +122,9 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
121
122
|
this.wrapWithWorkbench();
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.#resizeObserver.observe(this.parentElement);
|
|
128
|
-
}
|
|
129
|
-
this.updateScale();
|
|
130
|
-
|
|
131
|
-
// Initialize animations when component is first connected
|
|
132
|
-
// Regrettably, this doesn't work without the requestAnimationFrame
|
|
133
|
-
requestAnimationFrame(() => this.updateAnimations());
|
|
125
|
+
requestAnimationFrame(() => {
|
|
126
|
+
this.updateAnimations();
|
|
127
|
+
});
|
|
134
128
|
}
|
|
135
129
|
|
|
136
130
|
disconnectedCallback() {
|
|
@@ -138,53 +132,6 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
138
132
|
this.#resizeObserver?.disconnect();
|
|
139
133
|
}
|
|
140
134
|
|
|
141
|
-
private get displayedParent(): Element | null {
|
|
142
|
-
let displayedParent = this.parentElement;
|
|
143
|
-
while (
|
|
144
|
-
displayedParent &&
|
|
145
|
-
getComputedStyle(displayedParent).display === "contents"
|
|
146
|
-
) {
|
|
147
|
-
displayedParent = displayedParent.parentElement;
|
|
148
|
-
}
|
|
149
|
-
return displayedParent;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private updateScale() {
|
|
153
|
-
if (this.fit === "none") return;
|
|
154
|
-
|
|
155
|
-
const displayedParent = this.displayedParent;
|
|
156
|
-
if (!displayedParent) return;
|
|
157
|
-
|
|
158
|
-
// Get the natural size of the content
|
|
159
|
-
const contentWidth = this.clientWidth;
|
|
160
|
-
const contentHeight = this.clientHeight;
|
|
161
|
-
|
|
162
|
-
// Get the available space from displayed parent
|
|
163
|
-
const containerWidth = displayedParent.clientWidth;
|
|
164
|
-
const containerHeight = displayedParent.clientHeight;
|
|
165
|
-
|
|
166
|
-
// Calculate scale ratios
|
|
167
|
-
const widthRatio = containerWidth / contentWidth;
|
|
168
|
-
const heightRatio = containerHeight / contentHeight;
|
|
169
|
-
|
|
170
|
-
let scale: number;
|
|
171
|
-
if (this.fit === "contain") {
|
|
172
|
-
// Use height ratio for contain mode to ensure it fits vertically
|
|
173
|
-
scale = heightRatio;
|
|
174
|
-
|
|
175
|
-
// If width would overflow after scaling, use width ratio instead
|
|
176
|
-
if (contentWidth * scale > containerWidth) {
|
|
177
|
-
scale = widthRatio;
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
// cover
|
|
181
|
-
scale = Math.max(widthRatio, heightRatio);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Apply transform with fixed center origin
|
|
185
|
-
this.style.transform = `scale(${scale})`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
135
|
get storageKey() {
|
|
189
136
|
if (!this.id) {
|
|
190
137
|
throw new Error("Timegroup must have an id to use localStorage.");
|
|
@@ -192,6 +139,13 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
192
139
|
return `ef-timegroup-${this.id}`;
|
|
193
140
|
}
|
|
194
141
|
|
|
142
|
+
get intrinsicDurationMs() {
|
|
143
|
+
if (this.hasExplicitDuration) {
|
|
144
|
+
return this.explicitDurationMs;
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
195
149
|
get durationMs() {
|
|
196
150
|
switch (this.mode) {
|
|
197
151
|
case "fixed":
|
|
@@ -209,7 +163,7 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
209
163
|
case "contain": {
|
|
210
164
|
let maxDuration = 0;
|
|
211
165
|
for (const node of this.childTemporals) {
|
|
212
|
-
if (node.
|
|
166
|
+
if (node.intrinsicDurationMs !== undefined) {
|
|
213
167
|
maxDuration = Math.max(maxDuration, node.durationMs);
|
|
214
168
|
}
|
|
215
169
|
}
|
|
@@ -250,75 +204,7 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
250
204
|
}
|
|
251
205
|
|
|
252
206
|
private updateAnimations() {
|
|
253
|
-
this
|
|
254
|
-
"--ef-progress",
|
|
255
|
-
`${Math.max(0, Math.min(1, this.currentTimeMs / this.durationMs)) * 100}%`,
|
|
256
|
-
);
|
|
257
|
-
const timelineTimeMs = (this.rootTimegroup ?? this).currentTimeMs;
|
|
258
|
-
if (this.startTimeMs > timelineTimeMs || this.endTimeMs < timelineTimeMs) {
|
|
259
|
-
this.style.display = "none";
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
this.style.display = "";
|
|
263
|
-
const animations = this.getAnimations({ subtree: true });
|
|
264
|
-
this.style.setProperty("--ef-duration", `${this.durationMs}ms`);
|
|
265
|
-
this.style.setProperty(
|
|
266
|
-
"--ef-transition-duration",
|
|
267
|
-
`${this.parentTimegroup?.overlapMs ?? 0}ms`,
|
|
268
|
-
);
|
|
269
|
-
this.style.setProperty(
|
|
270
|
-
"--ef-transition-out-start",
|
|
271
|
-
`${this.durationMs - (this.parentTimegroup?.overlapMs ?? 0)}ms`,
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
for (const animation of animations) {
|
|
275
|
-
if (animation.playState === "running") {
|
|
276
|
-
animation.pause();
|
|
277
|
-
}
|
|
278
|
-
const effect = animation.effect;
|
|
279
|
-
if (!(effect && effect instanceof KeyframeEffect)) {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const target = effect.target;
|
|
283
|
-
// TODO: better generalize work avoidance for temporal elements
|
|
284
|
-
if (!target) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
if (target.closest("ef-timegroup") !== this) {
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Important to avoid going to the end of the animation
|
|
292
|
-
// or it will reset awkwardly.
|
|
293
|
-
if (isEFTemporal(target)) {
|
|
294
|
-
const timing = effect.getTiming();
|
|
295
|
-
const duration = Number(timing.duration) ?? 0;
|
|
296
|
-
const delay = Number(timing.delay);
|
|
297
|
-
const newTime = Math.floor(
|
|
298
|
-
Math.min(target.ownCurrentTimeMs, duration - 1 + delay),
|
|
299
|
-
);
|
|
300
|
-
if (Number.isNaN(newTime)) {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
animation.currentTime = newTime;
|
|
304
|
-
} else if (target) {
|
|
305
|
-
const nearestTimegroup = target.closest("ef-timegroup");
|
|
306
|
-
if (!nearestTimegroup) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
const timing = effect.getTiming();
|
|
310
|
-
const duration = Number(timing.duration) ?? 0;
|
|
311
|
-
const delay = Number(timing.delay);
|
|
312
|
-
const newTime = Math.floor(
|
|
313
|
-
Math.min(nearestTimegroup.ownCurrentTimeMs, duration - 1 + delay),
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
if (Number.isNaN(newTime)) {
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
animation.currentTime = newTime;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
207
|
+
updateAnimations(this);
|
|
322
208
|
}
|
|
323
209
|
|
|
324
210
|
get contextProvider() {
|
|
@@ -366,10 +252,6 @@ export class EFTimegroup extends EFTemporal(LitElement) {
|
|
|
366
252
|
workbench.append(filmstrip);
|
|
367
253
|
}
|
|
368
254
|
|
|
369
|
-
get hasOwnDuration() {
|
|
370
|
-
return true;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
255
|
get efElements() {
|
|
374
256
|
return Array.from(
|
|
375
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() {
|