@vectoriox/iox-ui 5.75.0 → 5.75.3
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 +27 -20
- package/fesm2022/vectoriox-iox-ui-media-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-scene-core.mjs +6 -5
- package/fesm2022/vectoriox-iox-ui-scene-core.mjs.map +1 -1
- package/media-core/index.d.ts +11 -6
- package/package.json +1 -1
- package/src/media-core/frames.ts +28 -20
- package/src/scene-core/scroll-driver.ts +6 -5
|
@@ -92,7 +92,7 @@ const FRAMES_SHOWING_ATTRIBUTE = 'data-iox-frames-showing';
|
|
|
92
92
|
* the subject showing twice, and then settling. Landing at least this fast keeps the end of the move a move.
|
|
93
93
|
*/
|
|
94
94
|
const FILM_LANDING_SPEED = 10;
|
|
95
|
-
/**
|
|
95
|
+
/** With no word from the driver on where the scroll is going, how fast a landing scroll is assumed to slow (per second). */
|
|
96
96
|
const FILM_LANDING_DECAY = 5;
|
|
97
97
|
/** How quickly the film catches up with a scroll that turned back while it was landing ahead of it (per second). */
|
|
98
98
|
const FILM_CATCH_RATE = 25;
|
|
@@ -124,15 +124,17 @@ function filmAt(wanted) {
|
|
|
124
124
|
}
|
|
125
125
|
/**
|
|
126
126
|
* One step of the film. Moving fast, it stands exactly where the scroll asks. Landing (slower than FILM_LANDING_SPEED
|
|
127
|
-
* pictures a second) it runs on at that speed to the whole picture
|
|
128
|
-
* never crawls through a dissolve, never stops between two pictures, and never steps back
|
|
127
|
+
* pictures a second) it runs on at that speed to the whole picture nearest where the scroll will stop, and stands there:
|
|
128
|
+
* it never crawls through a dissolve, never stops between two pictures, and never steps back over a picture it reached
|
|
129
|
+
* unless the scroll turns. `aim` is where the scroll is heading, when the driver knows it (its undamped target); without it, it is guessed.
|
|
129
130
|
*/
|
|
130
|
-
function stepFilm(motion, wanted, seconds, last) {
|
|
131
|
+
function stepFilm(motion, wanted, seconds, last, aim) {
|
|
131
132
|
const sameFrame = seconds < FILM_SAME_FRAME_SECONDS;
|
|
132
133
|
const step = Math.min(FILM_MAX_STEP_SECONDS, seconds);
|
|
133
134
|
const moved = wanted - motion.wanted;
|
|
134
135
|
const speed = sameFrame ? motion.speed : motion.speed + (moved / step - motion.speed) * (1 - Math.exp(-FILM_SPEED_RATE * step));
|
|
135
|
-
const
|
|
136
|
+
const heading = aim === undefined ? moved : aim - wanted;
|
|
137
|
+
const direction = Math.abs(heading) > FILM_EPSILON ? (heading > 0 ? 1 : -1) : Math.abs(moved) > FILM_EPSILON ? (moved > 0 ? 1 : -1) : motion.direction;
|
|
136
138
|
// Moving: with the scroll, but never back over a picture a landing already reached unless the scroll turned.
|
|
137
139
|
if (Math.abs(speed) >= FILM_LANDING_SPEED) {
|
|
138
140
|
const position = direction > 0 ? Math.max(wanted, Math.min(motion.position, Math.ceil(wanted))) : Math.min(wanted, Math.max(motion.position, Math.floor(wanted)));
|
|
@@ -141,18 +143,19 @@ function stepFilm(motion, wanted, seconds, last) {
|
|
|
141
143
|
if (sameFrame)
|
|
142
144
|
return { ...motion, wanted, direction };
|
|
143
145
|
const clamp = (value) => Math.min(last, Math.max(0, value));
|
|
144
|
-
const stop = wanted + speed / FILM_LANDING_DECAY;
|
|
145
|
-
|
|
146
|
-
const
|
|
146
|
+
const stop = aim ?? wanted + speed / FILM_LANDING_DECAY;
|
|
147
|
+
// The picture nearest the stop, unless reaching it would show a step back: then the next whole one on the way.
|
|
148
|
+
const nearest = Math.round(stop);
|
|
149
|
+
const back = (motion.position - nearest) * direction > 1 / FRAME_BLEND_STEPS;
|
|
150
|
+
const goal = clamp(back ? (direction > 0 ? Math.ceil(motion.position) : Math.floor(motion.position)) : nearest);
|
|
147
151
|
const behind = (wanted - motion.position) * direction;
|
|
148
152
|
const reach = Math.max(FILM_LANDING_SPEED * step, behind > 0 ? behind * (1 - Math.exp(-FILM_CATCH_RATE * step)) : 0);
|
|
149
153
|
const position = direction > 0 ? Math.min(goal, motion.position + reach) : Math.max(goal, motion.position - reach);
|
|
150
154
|
return { position, wanted, speed, direction };
|
|
151
155
|
}
|
|
152
|
-
/** Whether a film has landed: still, on
|
|
156
|
+
/** Whether a film has landed: still, on a whole picture beside where the scroll stands. */
|
|
153
157
|
function filmLanded(motion) {
|
|
154
|
-
|
|
155
|
-
return Math.abs(motion.speed) < 0.01 && Number.isInteger(motion.position) && past > -FILM_EPSILON && past < 1;
|
|
158
|
+
return Math.abs(motion.speed) < 0.01 && Number.isInteger(motion.position) && Math.abs(motion.position - motion.wanted) < 1;
|
|
156
159
|
}
|
|
157
160
|
/** The picture at `played` % of a set of `count`. */
|
|
158
161
|
function frameIndexAt(played, count) {
|
|
@@ -279,26 +282,30 @@ class FramePlayer {
|
|
|
279
282
|
}, { rootMargin: NEAR_MARGIN });
|
|
280
283
|
this.observer.observe(options.near ?? el);
|
|
281
284
|
}
|
|
282
|
-
/**
|
|
283
|
-
|
|
284
|
-
|
|
285
|
+
/**
|
|
286
|
+
* Stand at `played` %, on the way to `aim` % when the caller knows where the scroll is going. `now` is the caller's
|
|
287
|
+
* frame time: the player keeps time by frames, so a frame asked twice moves once however long the page took.
|
|
288
|
+
*/
|
|
289
|
+
seek(played, aim, now) {
|
|
290
|
+
if (played === this.played && aim === this.aim)
|
|
285
291
|
return;
|
|
286
292
|
this.played = played;
|
|
287
|
-
this.
|
|
293
|
+
this.aim = aim;
|
|
294
|
+
this.step(now ?? this.host.performance?.now() ?? 0);
|
|
288
295
|
}
|
|
289
296
|
/** One step toward the moment asked for, drawn now; a landing goes on by itself until it stands on a whole picture. */
|
|
290
|
-
step() {
|
|
297
|
+
step(now) {
|
|
291
298
|
if (this.destroyed || this.played === null)
|
|
292
299
|
return;
|
|
293
300
|
const wanted = framePosition(this.played, this.info.count);
|
|
294
|
-
const
|
|
295
|
-
this.motion = this.motion ? stepFilm(this.motion, wanted, (now - this.steppedAt) / 1000, this.info.count - 1) : filmAt(wanted);
|
|
301
|
+
const aim = this.aim === undefined ? undefined : framePosition(this.aim, this.info.count);
|
|
302
|
+
this.motion = this.motion ? stepFilm(this.motion, wanted, (now - this.steppedAt) / 1000, this.info.count - 1, aim) : filmAt(wanted);
|
|
296
303
|
this.steppedAt = now;
|
|
297
304
|
this.draw();
|
|
298
305
|
if (!filmLanded(this.motion) && this.landingFrame === null)
|
|
299
|
-
this.landingFrame = this.host.requestAnimationFrame(
|
|
306
|
+
this.landingFrame = this.host.requestAnimationFrame(time => {
|
|
300
307
|
this.landingFrame = null;
|
|
301
|
-
this.step();
|
|
308
|
+
this.step(time);
|
|
302
309
|
});
|
|
303
310
|
}
|
|
304
311
|
stopLanding() {
|