@twick/2d 0.15.15 → 0.15.17
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/index.cjs +56 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +56 -7
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -86,8 +86,18 @@ declare abstract class Media extends Rect {
|
|
|
86
86
|
protected abstract mediaElement(): HTMLMediaElement;
|
|
87
87
|
protected abstract seekedMedia(): HTMLMediaElement;
|
|
88
88
|
protected abstract fastSeekedMedia(): HTMLMediaElement;
|
|
89
|
+
/**
|
|
90
|
+
* Sync the underlying media element to the given time (e.g. draw/playback time).
|
|
91
|
+
* When `time` is passed (e.g. from Scene2D.draw), that value is used so sync
|
|
92
|
+
* does not depend on the node's time signal (which may only update on projectData change).
|
|
93
|
+
* When omitted, falls back to this.time().
|
|
94
|
+
* Uses skipCollectPromise so we don't leave a promise in DependencyContext.
|
|
95
|
+
*/
|
|
96
|
+
syncToCurrentTime(time?: number): void;
|
|
89
97
|
protected abstract draw(context: CanvasRenderingContext2D): Promise<void>;
|
|
90
|
-
protected setCurrentTime(value: number
|
|
98
|
+
protected setCurrentTime(value: number, options?: {
|
|
99
|
+
skipCollectPromise?: boolean;
|
|
100
|
+
}): void;
|
|
91
101
|
setVolume(volume: number): void;
|
|
92
102
|
protected amplify(node: HTMLMediaElement, volume: number): void;
|
|
93
103
|
protected setPlaybackRate(playbackRate: number): void;
|
|
@@ -2528,6 +2538,11 @@ declare class Scene2D extends GeneratorScene<View2D> implements Inspectable {
|
|
|
2528
2538
|
getNode(key: any): Node | null;
|
|
2529
2539
|
getDetachedNodes(): Generator<Node, void, unknown>;
|
|
2530
2540
|
getMediaAssets(): Array<AssetInfo>;
|
|
2541
|
+
/**
|
|
2542
|
+
* Seek all registered Media nodes to the current playback time.
|
|
2543
|
+
* Passes draw time (playback.time) so media sync does not depend on node time signal.
|
|
2544
|
+
*/
|
|
2545
|
+
private syncAllMediaToCurrentTime;
|
|
2531
2546
|
stopAllMedia(): void;
|
|
2532
2547
|
adjustVolume(volumeScale: number): void;
|
|
2533
2548
|
protected recreateView(): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -86,8 +86,18 @@ declare abstract class Media extends Rect {
|
|
|
86
86
|
protected abstract mediaElement(): HTMLMediaElement;
|
|
87
87
|
protected abstract seekedMedia(): HTMLMediaElement;
|
|
88
88
|
protected abstract fastSeekedMedia(): HTMLMediaElement;
|
|
89
|
+
/**
|
|
90
|
+
* Sync the underlying media element to the given time (e.g. draw/playback time).
|
|
91
|
+
* When `time` is passed (e.g. from Scene2D.draw), that value is used so sync
|
|
92
|
+
* does not depend on the node's time signal (which may only update on projectData change).
|
|
93
|
+
* When omitted, falls back to this.time().
|
|
94
|
+
* Uses skipCollectPromise so we don't leave a promise in DependencyContext.
|
|
95
|
+
*/
|
|
96
|
+
syncToCurrentTime(time?: number): void;
|
|
89
97
|
protected abstract draw(context: CanvasRenderingContext2D): Promise<void>;
|
|
90
|
-
protected setCurrentTime(value: number
|
|
98
|
+
protected setCurrentTime(value: number, options?: {
|
|
99
|
+
skipCollectPromise?: boolean;
|
|
100
|
+
}): void;
|
|
91
101
|
setVolume(volume: number): void;
|
|
92
102
|
protected amplify(node: HTMLMediaElement, volume: number): void;
|
|
93
103
|
protected setPlaybackRate(playbackRate: number): void;
|
|
@@ -2528,6 +2538,11 @@ declare class Scene2D extends GeneratorScene<View2D> implements Inspectable {
|
|
|
2528
2538
|
getNode(key: any): Node | null;
|
|
2529
2539
|
getDetachedNodes(): Generator<Node, void, unknown>;
|
|
2530
2540
|
getMediaAssets(): Array<AssetInfo>;
|
|
2541
|
+
/**
|
|
2542
|
+
* Seek all registered Media nodes to the current playback time.
|
|
2543
|
+
* Passes draw time (playback.time) so media sync does not depend on node time signal.
|
|
2544
|
+
*/
|
|
2545
|
+
private syncAllMediaToCurrentTime;
|
|
2531
2546
|
stopAllMedia(): void;
|
|
2532
2547
|
adjustVolume(volumeScale: number): void;
|
|
2533
2548
|
protected recreateView(): void;
|
package/dist/index.js
CHANGED
|
@@ -5402,7 +5402,7 @@ var Media = class extends Rect {
|
|
|
5402
5402
|
this.lastTime = -1;
|
|
5403
5403
|
this.isSchedulingPlay = false;
|
|
5404
5404
|
if (!this.awaitCanPlay()) {
|
|
5405
|
-
this.scheduleSeek(this.time());
|
|
5405
|
+
setTimeout(() => this.scheduleSeek(this.time()), 0);
|
|
5406
5406
|
}
|
|
5407
5407
|
if (props.play) {
|
|
5408
5408
|
this.play();
|
|
@@ -5447,13 +5447,35 @@ var Media = class extends Rect {
|
|
|
5447
5447
|
completion() {
|
|
5448
5448
|
return this.clampTime(this.time()) / this.getDuration();
|
|
5449
5449
|
}
|
|
5450
|
-
|
|
5450
|
+
/**
|
|
5451
|
+
* Sync the underlying media element to the given time (e.g. draw/playback time).
|
|
5452
|
+
* When `time` is passed (e.g. from Scene2D.draw), that value is used so sync
|
|
5453
|
+
* does not depend on the node's time signal (which may only update on projectData change).
|
|
5454
|
+
* When omitted, falls back to this.time().
|
|
5455
|
+
* Uses skipCollectPromise so we don't leave a promise in DependencyContext.
|
|
5456
|
+
*/
|
|
5457
|
+
syncToCurrentTime(time) {
|
|
5458
|
+
const syncTime = time ?? this.time();
|
|
5459
|
+
this.setCurrentTime(syncTime, { skipCollectPromise: true });
|
|
5460
|
+
}
|
|
5461
|
+
setCurrentTime(value, options) {
|
|
5451
5462
|
try {
|
|
5452
5463
|
const media = this.mediaElement();
|
|
5453
|
-
|
|
5464
|
+
const key = this.key ?? "media";
|
|
5465
|
+
if (media.readyState < 2) {
|
|
5466
|
+
this.lastTime = value;
|
|
5467
|
+
this.time(value);
|
|
5468
|
+
this.waitForCanPlay(media, () => {
|
|
5469
|
+
media.currentTime = value;
|
|
5470
|
+
this.lastTime = value;
|
|
5471
|
+
this.time(value);
|
|
5472
|
+
});
|
|
5473
|
+
return;
|
|
5474
|
+
}
|
|
5454
5475
|
media.currentTime = value;
|
|
5455
5476
|
this.lastTime = value;
|
|
5456
|
-
|
|
5477
|
+
this.time(value);
|
|
5478
|
+
if (media.seeking && !options?.skipCollectPromise) {
|
|
5457
5479
|
DependencyContext4.collectPromise(
|
|
5458
5480
|
new Promise((resolve) => {
|
|
5459
5481
|
const listener = () => {
|
|
@@ -5466,6 +5488,7 @@ var Media = class extends Rect {
|
|
|
5466
5488
|
}
|
|
5467
5489
|
} catch (error) {
|
|
5468
5490
|
this.lastTime = value;
|
|
5491
|
+
this.time(value);
|
|
5469
5492
|
}
|
|
5470
5493
|
}
|
|
5471
5494
|
setVolume(volume) {
|
|
@@ -10397,11 +10420,13 @@ var Video = class extends Media {
|
|
|
10397
10420
|
fastSeekedVideo() {
|
|
10398
10421
|
const video = this.video();
|
|
10399
10422
|
const time = this.clampTime(this.time());
|
|
10423
|
+
const playing = this.playing() && time < video.duration && video.playbackRate > 0;
|
|
10424
|
+
const wouldResetToZero = time < 0.5 && video.currentTime > 1 && Math.abs(video.currentTime - this.lastTime) < 0.5;
|
|
10425
|
+
const outOfSyncBig = Math.abs(video.currentTime - time) > 1;
|
|
10400
10426
|
video.playbackRate = this.playbackRate();
|
|
10401
10427
|
if (this.lastTime === time) {
|
|
10402
10428
|
return video;
|
|
10403
10429
|
}
|
|
10404
|
-
const playing = this.playing() && time < video.duration && video.playbackRate > 0;
|
|
10405
10430
|
if (playing) {
|
|
10406
10431
|
if (video.paused) {
|
|
10407
10432
|
DependencyContext8.collectPromise(video.play());
|
|
@@ -10411,10 +10436,11 @@ var Video = class extends Media {
|
|
|
10411
10436
|
video.pause();
|
|
10412
10437
|
}
|
|
10413
10438
|
}
|
|
10414
|
-
if (
|
|
10439
|
+
if (wouldResetToZero) {
|
|
10440
|
+
} else if (outOfSyncBig) {
|
|
10415
10441
|
this.setCurrentTime(time);
|
|
10416
10442
|
} else if (!playing) {
|
|
10417
|
-
|
|
10443
|
+
this.setCurrentTime(time);
|
|
10418
10444
|
}
|
|
10419
10445
|
return video;
|
|
10420
10446
|
}
|
|
@@ -10641,6 +10667,9 @@ var Scene2D = class extends GeneratorScene {
|
|
|
10641
10667
|
context.save();
|
|
10642
10668
|
this.renderLifecycle.dispatch([SceneRenderEvent.BeginRender, context]);
|
|
10643
10669
|
this.getView().playbackState(this.playback.state).globalTime(this.playback.time).fps(this.playback.fps);
|
|
10670
|
+
if (this.playback.state === PlaybackState5.Paused) {
|
|
10671
|
+
this.syncAllMediaToCurrentTime();
|
|
10672
|
+
}
|
|
10644
10673
|
await this.getView().render(context);
|
|
10645
10674
|
this.renderLifecycle.dispatch([SceneRenderEvent.FinishRender, context]);
|
|
10646
10675
|
context.restore();
|
|
@@ -10771,6 +10800,26 @@ var Scene2D = class extends GeneratorScene {
|
|
|
10771
10800
|
);
|
|
10772
10801
|
return returnObjects;
|
|
10773
10802
|
}
|
|
10803
|
+
/**
|
|
10804
|
+
* Seek all registered Media nodes to the current playback time.
|
|
10805
|
+
* Passes draw time (playback.time) so media sync does not depend on node time signal.
|
|
10806
|
+
*/
|
|
10807
|
+
syncAllMediaToCurrentTime() {
|
|
10808
|
+
const drawTime = this.playback.time;
|
|
10809
|
+
const mediaNodes = Array.from(this.registeredNodes.values()).filter(
|
|
10810
|
+
(node) => node instanceof Media
|
|
10811
|
+
);
|
|
10812
|
+
for (const media of mediaNodes) {
|
|
10813
|
+
try {
|
|
10814
|
+
media.syncToCurrentTime(drawTime);
|
|
10815
|
+
} catch (e) {
|
|
10816
|
+
this.logger.warn({
|
|
10817
|
+
message: `syncAllMediaToCurrentTime: skipped node ${media.key ?? "unknown"}`,
|
|
10818
|
+
object: e
|
|
10819
|
+
});
|
|
10820
|
+
}
|
|
10821
|
+
}
|
|
10822
|
+
}
|
|
10774
10823
|
stopAllMedia() {
|
|
10775
10824
|
const playingMedia = Array.from(this.registeredNodes.values()).filter((node) => node instanceof Media).filter((video) => video.isPlaying());
|
|
10776
10825
|
for (const media of playingMedia) {
|