@vectoriox/iox-ui 5.74.0 → 5.74.2
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/fesm2022/vectoriox-iox-ui-media-core.mjs +43 -1
- package/fesm2022/vectoriox-iox-ui-media-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-scene-core.mjs +2 -0
- package/fesm2022/vectoriox-iox-ui-scene-core.mjs.map +1 -1
- package/media-core/index.d.ts +9 -1
- package/package.json +1 -1
- package/scene-core/index.d.ts +1 -1
- package/src/media-core/frames.ts +40 -0
- package/src/scene-core/scene.ts +2 -0
- package/styles/iox-design.css +1 -0
package/src/media-core/frames.ts
CHANGED
|
@@ -108,6 +108,13 @@ export function framePosition(played: number, count: number): number {
|
|
|
108
108
|
return (Math.min(100, Math.max(0, played)) / 100) * (count - 1);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/** On a Video whose pictures are drawing: the poster has done its job and steps aside. */
|
|
112
|
+
export const FRAMES_SHOWING_ATTRIBUTE = 'data-iox-frames-showing';
|
|
113
|
+
|
|
114
|
+
/** How long a stopped scroll waits before settling on a whole picture, and how fast it gets there per frame. */
|
|
115
|
+
const REST_AFTER_MS = 140;
|
|
116
|
+
const REST_RATE = 0.25;
|
|
117
|
+
|
|
111
118
|
/** How finely two neighbours mix. Finer than the eye reads, and a scroll that moves less than a step draws nothing. */
|
|
112
119
|
export const FRAME_BLEND_STEPS = 32;
|
|
113
120
|
|
|
@@ -247,6 +254,9 @@ export interface FramePlayerOptions {
|
|
|
247
254
|
* Browser only. It writes and never reads, so the driver can call it in its write phase.
|
|
248
255
|
*/
|
|
249
256
|
export class FramePlayer {
|
|
257
|
+
private played: number | null = null;
|
|
258
|
+
private restTimer: number | null = null;
|
|
259
|
+
private restFrame: number | null = null;
|
|
250
260
|
private readonly set: FrameSet;
|
|
251
261
|
private readonly info: FrameSetInfo;
|
|
252
262
|
private readonly images: Array<HTMLImageElement | null>;
|
|
@@ -284,8 +294,34 @@ export class FramePlayer {
|
|
|
284
294
|
|
|
285
295
|
/** Stand at `played` %. */
|
|
286
296
|
seek(played: number): void {
|
|
297
|
+
if (played === this.played) return;
|
|
298
|
+
this.played = played;
|
|
299
|
+
this.stopResting();
|
|
287
300
|
this.position = framePosition(played, this.info.count);
|
|
288
301
|
this.draw();
|
|
302
|
+
this.restTimer = this.host.setTimeout?.(() => this.rest(), REST_AFTER_MS) ?? null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** A stopped scroll must not stand on two pictures at once: a moving subject would show twice. It settles on the nearer. */
|
|
306
|
+
private rest(): void {
|
|
307
|
+
this.restTimer = null;
|
|
308
|
+
const target = Math.round(this.position);
|
|
309
|
+
const step = (): void => {
|
|
310
|
+
this.restFrame = null;
|
|
311
|
+
if (this.destroyed) return;
|
|
312
|
+
const left = target - this.position;
|
|
313
|
+
this.position = Math.abs(left) < 1 / FRAME_BLEND_STEPS ? target : this.position + left * REST_RATE;
|
|
314
|
+
this.draw();
|
|
315
|
+
if (this.position !== target) this.restFrame = this.host.requestAnimationFrame(step);
|
|
316
|
+
};
|
|
317
|
+
step();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private stopResting(): void {
|
|
321
|
+
if (this.restTimer !== null) this.host.clearTimeout?.(this.restTimer);
|
|
322
|
+
if (this.restFrame !== null) this.host.cancelAnimationFrame?.(this.restFrame);
|
|
323
|
+
this.restTimer = null;
|
|
324
|
+
this.restFrame = null;
|
|
289
325
|
}
|
|
290
326
|
|
|
291
327
|
/** Whether a re-render took the canvas away with the markup it sat in. */
|
|
@@ -295,6 +331,8 @@ export class FramePlayer {
|
|
|
295
331
|
|
|
296
332
|
destroy(): void {
|
|
297
333
|
this.destroyed = true;
|
|
334
|
+
this.stopResting();
|
|
335
|
+
this.el.removeAttribute(FRAMES_SHOWING_ATTRIBUTE);
|
|
298
336
|
this.observer?.disconnect();
|
|
299
337
|
this.observer = null;
|
|
300
338
|
for (const image of this.pending) {
|
|
@@ -368,6 +406,8 @@ export class FramePlayer {
|
|
|
368
406
|
context.drawImage(this.images[blend.to]!, 0, 0, width, height);
|
|
369
407
|
context.globalAlpha = 1;
|
|
370
408
|
}
|
|
409
|
+
// Over a transparent film the poster would show through: a second, stiller subject behind the moving one.
|
|
410
|
+
if (!this.drawn) this.el.setAttribute(FRAMES_SHOWING_ATTRIBUTE, '');
|
|
371
411
|
this.drawn = blend;
|
|
372
412
|
}
|
|
373
413
|
|
package/src/scene-core/scene.ts
CHANGED
|
@@ -310,6 +310,8 @@ export function applySceneFirstFrame(el: HTMLElement | null | undefined, node: S
|
|
|
310
310
|
export const SCENE_FLOW_RULES = `.iox-role-scene{height:auto!important}`
|
|
311
311
|
+ `.iox-role-scene-stage{position:relative!important;top:auto!important;height:auto!important;overflow:visible!important;container-type:normal!important}`
|
|
312
312
|
+ `[${SceneAttribute.Tracks}]{transform:none!important;opacity:1!important;clip-path:none!important;pointer-events:auto!important}`
|
|
313
|
+
// A row the scroll slid sideways has no scroll to slide it now: it wraps, or its last items run off the side of a phone.
|
|
314
|
+
+ `[${SceneAttribute.Tracks}]{max-width:100%!important;flex-wrap:wrap!important}`
|
|
313
315
|
// A Video the scroll plays stands as its poster, at the poster's own height: a percent of a stage that now flows is nothing.
|
|
314
316
|
+ `[${VideoAttribute.Frames}]{height:auto!important}`
|
|
315
317
|
+ `[${VideoAttribute.Frames}]>.${VideoClass.Poster},[${VideoAttribute.Frames}]>picture>.${VideoClass.Poster}{position:static!important;height:auto!important}`;
|
package/styles/iox-design.css
CHANGED
|
@@ -359,6 +359,7 @@ dialog[data-iox-dialog]:not([open]) {
|
|
|
359
359
|
position: absolute; inset: 0; width: 100%; height: 100%; border: 0; margin: 0; display: block; object-fit: cover;
|
|
360
360
|
}
|
|
361
361
|
.iox-video > .iox-video__poster, .iox-video > picture > .iox-video__poster { z-index: -1; }
|
|
362
|
+
.iox-video[data-iox-frames-showing] > .iox-video__poster, .iox-video[data-iox-frames-showing] > picture > .iox-video__poster { visibility: hidden; }
|
|
362
363
|
.iox-video > .iox-video__media { object-fit: contain; background-color: var(--iox-inverse-bg); }
|
|
363
364
|
.iox-video[data-iox-video-mode='background'] > .iox-video__media { object-fit: cover; }
|
|
364
365
|
.iox-video__play {
|