feat(ads): carry the banner scroll position across navigation
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 49s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 49s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
Per the owner's idea: instead of moving the banner out of the per-screen header (which would change its position), remember the banner's "life stage" and resume it on the next screen. The engine already keeps the message + rotation timing; this adds the scroll offset: - bannerEngine tracks the in-flight scroll (target, duration, start). On attach, if a scroll is still running, it computes the current offset and calls the new host's resumeScroll(fromTx, toPx, remaining) — the view jumps to the carried offset and continues to the end over the remaining time, instead of restarting at the left. - A finished scroll is left at its end; the rotator's own loop then takes over. Verified: spot-checked in the browser (a long message at offset -785 resumes at -788 on the next screen, not 0) and unit-tested (attach mid-scroll calls resumeScroll with a partial offset and the remaining duration).
This commit is contained in:
@@ -10,15 +10,20 @@ let rotator: Rotator | null = null;
|
||||
let mounted: BannerHost | null = null;
|
||||
let current = '';
|
||||
let key = '';
|
||||
// The in-flight horizontal scroll of the current message, so a view mounted by navigation can
|
||||
// resume it from the same offset instead of restarting at the left. Null when not scrolling.
|
||||
let activeScroll: { toPx: number; dur: number; start: number } | null = null;
|
||||
|
||||
// proxy is the rotator's host: it records the current message (so a freshly-mounted view can
|
||||
// resync to it) and forwards every effect to the currently-attached DOM host, if any.
|
||||
// proxy is the rotator's host: it records the current message + scroll (so a freshly-mounted view
|
||||
// can resume them) and forwards every effect to the currently-attached DOM host, if any.
|
||||
const proxy: BannerHost = {
|
||||
show(md) {
|
||||
current = md;
|
||||
activeScroll = null;
|
||||
mounted?.show(md);
|
||||
},
|
||||
resetScroll() {
|
||||
activeScroll = null;
|
||||
mounted?.resetScroll();
|
||||
},
|
||||
hide(durationMs) {
|
||||
@@ -28,8 +33,12 @@ const proxy: BannerHost = {
|
||||
return mounted?.overflowPx() ?? 0;
|
||||
},
|
||||
scrollTo(toPx, durationMs) {
|
||||
activeScroll = { toPx, dur: durationMs, start: Date.now() };
|
||||
mounted?.scrollTo(toPx, durationMs);
|
||||
},
|
||||
resumeScroll(fromTx, toPx, durationMs) {
|
||||
mounted?.resumeScroll(fromTx, toPx, durationMs);
|
||||
},
|
||||
};
|
||||
|
||||
// bannerKey identifies a campaigns+timings set so configureBanner restarts the cycle only on a
|
||||
@@ -70,6 +79,16 @@ export function bannerCurrent(): string {
|
||||
*/
|
||||
export function attachBannerHost(host: BannerHost): void {
|
||||
mounted = host;
|
||||
// Resume the current message's scroll from where it had reached, so navigation does not restart
|
||||
// a long message at the left. Only while a scroll is still in flight; a finished scroll is left
|
||||
// at its end and the rotator's own loop takes over.
|
||||
if (activeScroll) {
|
||||
const elapsed = Date.now() - activeScroll.start;
|
||||
if (elapsed < activeScroll.dur) {
|
||||
const progress = elapsed / activeScroll.dur;
|
||||
host.resumeScroll(-activeScroll.toPx * progress, activeScroll.toPx, activeScroll.dur - elapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user