Fix video sizing and replace fixed landing timeout with playback-aware g
- Introduce `attachClip` to set volume, cover-fit, and re-fit on the first presented frame (`created`) so clips no longer render ~3.4× zoomed in due to v4 placeholder sizes. - Use `setScale` instead of `setDisplaySize` in `fitCover`, which is stable across frame swaps that preserve scale rather than size. - Drop the blanket 8s safety valve that cut ~15s landing clips in half; replace with two targeted guards: a 5s stalled check (no playback progress) and a duration-based cap (duration + 5s margin).
This commit is contained in:
parent
dd7000638a
commit
69875c1ff0
|
|
@ -87,8 +87,7 @@ export class SurfaceScene extends Phaser.Scene {
|
|||
// v4: add.video(x, y, key) — the key is the LAST argument (it loads the
|
||||
// cached clip and attaches the <video> element).
|
||||
const v = this.add.video(0, 0, this.landKey).setOrigin(0.5).setDepth(10);
|
||||
this.fitCover(v);
|
||||
v.setVolume(this.clipVolume());
|
||||
this.attachClip(v);
|
||||
|
||||
this.landVideo = v;
|
||||
// Fire it straight away. If the browser's autoplay policy locks it
|
||||
|
|
@ -99,11 +98,28 @@ export class SurfaceScene extends Phaser.Scene {
|
|||
v.play();
|
||||
v.on('complete', () => this.startSurface()); // played once → surface
|
||||
v.on('error', () => this.startSurface()); // broken clip → don't strand the player
|
||||
// Safety valve: a stalled element (neither complete nor error) still
|
||||
// gets the player to the deck.
|
||||
this.time.delayedCall(8000, () => {
|
||||
if (this.landVideo === v && this.phase === 'landing') this.startSurface();
|
||||
|
||||
const el = v.video;
|
||||
// The clip must play to its END before the surface stage (loop clip +
|
||||
// deck), so no fixed "8s" valve — the current clips are ~15s and a
|
||||
// constant would cut the landing in half. Two targeted guards instead:
|
||||
// 1. STALLED — a clip that never starts moving (autoplay locked and
|
||||
// never unlocked, decode failure, …) must not hold the deck: after
|
||||
// a grace period with no playback progress, advance anyway.
|
||||
// 2. CAP — 'ended' is expected by duration + margin; if the element
|
||||
// goes silent before it, advance anyway.
|
||||
const playing = () => !!el && (el.currentTime > 0.1 || !el.paused);
|
||||
this.time.delayedCall(5000, () => {
|
||||
if (this.landVideo === v && this.phase === 'landing' && !playing()) {
|
||||
this.startSurface();
|
||||
}
|
||||
});
|
||||
const durS = Number(el && el.duration);
|
||||
if (Number.isFinite(durS) && durS > 0) {
|
||||
this.time.delayedCall(durS * 1000 + 5000, () => {
|
||||
if (this.landVideo === v && this.phase === 'landing') this.startSurface();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
|
@ -121,8 +137,7 @@ export class SurfaceScene extends Phaser.Scene {
|
|||
|
||||
if (this.hasVideo(this.surfaceKey)) {
|
||||
const s = this.add.video(0, 0, this.surfaceKey).setOrigin(0.5).setDepth(10);
|
||||
this.fitCover(s);
|
||||
s.setVolume(this.clipVolume());
|
||||
this.attachClip(s);
|
||||
s.setLoop(true);
|
||||
s.play();
|
||||
this.surfaceVideo = s;
|
||||
|
|
@ -286,15 +301,37 @@ export class SurfaceScene extends Phaser.Scene {
|
|||
return !!(c && typeof c.has === 'function' && c.has(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cover-fit a clip to the screen and keep it that way.
|
||||
*
|
||||
* v4 quirk: the video object's bookkeeping size (width/height, and even
|
||||
* frame.realWidth) is a placeholder until the first presented frame
|
||||
* lands — sizing against it (setDisplaySize divides by that width) ends
|
||||
* up ~3.4× too big, because the frame swap preserves the SCALE, not the
|
||||
* size. That's how the clips rendered heavily zoomed in. So:
|
||||
* - fit now with the best known dimensions (a placeholder-free guess),
|
||||
* - re-fit on 'created', which fires on the first presented frame and
|
||||
* carries the clip's true (w, h) — after that, frame × scale is
|
||||
* authoritative and stable for the clip's whole life.
|
||||
*/
|
||||
attachClip(v) {
|
||||
v.setVolume(this.clipVolume());
|
||||
this.fitCover(v);
|
||||
v.on('created', (vv, w, h) => {
|
||||
if (vv === v) this.fitCover(vv, w, h);
|
||||
});
|
||||
}
|
||||
|
||||
/** Cover-scale: the clip fills the screen, cropping whatever overflows. */
|
||||
fitCover(v) {
|
||||
const vw = (v.video && v.video.width) || v.width || 864;
|
||||
const vh = (v.video && v.video.height) || v.height || 480;
|
||||
fitCover(v, iw = 0, ih = 0) {
|
||||
const el = v.video;
|
||||
const vw = iw || (el && (el.videoWidth || el.width)) || (v.frame && v.frame.realWidth) || 864;
|
||||
const vh = ih || (el && (el.videoHeight || el.height)) || (v.frame && v.frame.realHeight) || 480;
|
||||
const W = this.scale.width;
|
||||
const H = this.scale.height;
|
||||
const s = Math.max(W / vw, H / vh);
|
||||
v.setPosition(W / 2, H / 2);
|
||||
v.setDisplaySize(vw * s, vh * s);
|
||||
v.setScale(s);
|
||||
}
|
||||
|
||||
clipVolume() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue