fix(ads): banner truly continuous across navigation + re-measure on resize
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 48s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 48s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
The previous engine kept the scheduler running but the view re-`show()`-ed the
current message on every (re)mount, replaying the fade on each navigation — which
looked like the cycle restarting (especially for a single message). Now:
- A mounted AdBanner reads the engine's live message (bannerCurrent) and renders
it immediately, with no fade; attach no longer re-shows. Only a real advance
fades. Verified: opacity stays 1.0 across a navigation, message preserved.
- The fade is manual opacity on a .fadewrap layer (not transition:fade), kept
independent of the scroll (inner track transform), so a long message still
fades at both ends and a {#key} remount cannot force an intro fade.
- A viewport size change (portrait↔landscape) re-measures the current message
(remeasureBanner on resize/orientationchange, debounced) so the scroll
re-evaluates for the new width — the owner accepts the restart on resize.
Rotator gains restart(); engine gains bannerCurrent()/remeasureBanner().
Engine continuity + remeasure unit-tested.
This commit is contained in:
+14
-10
@@ -209,16 +209,20 @@ the profile to show or hide it in place.
|
||||
|
||||
The rotation runs in a **persistent module engine** (`lib/bannerEngine`): the scheduler and timer
|
||||
live outside the components, so navigating between screens (which remounts the view) **continues the
|
||||
cycle** rather than restarting it — each mounted `AdBanner` only attaches as the DOM host and resyncs
|
||||
to the live message. A **smooth weighted round-robin** (`createScheduler`) picks the next message:
|
||||
campaigns compete by their weight (each appears its weight share per cycle, evenly interleaved, not
|
||||
at random), and a campaign's own messages advance round-robin. One message fades in (`fadeInMs`),
|
||||
holds `holdMs` (a message wider than the strip pauses `edgePauseMs`, scrolls to its right edge at
|
||||
`scrollPxPerSec`, pauses, and repeats while under `holdMs`), then — with more than one message —
|
||||
fades out (`fadeOutMs`), waits `gapMs`, and the next fades in. The fade (a `{#if}` `transition:fade`
|
||||
layer) is independent of the scroll (the inner track's transform), so a long, scrolling message
|
||||
still fades at both ends. A lone message stays put (a long one keeps scrolling). All timings are
|
||||
operator-set (`/_gm/banner-settings`). Under **reduce-motion** the fades collapse to an instant swap
|
||||
cycle** rather than restarting it. A mounted `AdBanner` reads the engine's live message at init and
|
||||
renders it **immediately, without a fade** (`bannerCurrent`), and attaches only as the DOM host — so
|
||||
a screen change does not replay the fade; only a real message advance fades. A **smooth weighted
|
||||
round-robin** (`createScheduler`) picks the next message: campaigns compete by their weight (each
|
||||
appears its weight share per cycle, evenly interleaved, not at random), and a campaign's own messages
|
||||
advance round-robin. One message fades in (`fadeInMs`), holds `holdMs` (a message wider than the
|
||||
strip pauses `edgePauseMs`, scrolls to its right edge at `scrollPxPerSec`, pauses, and repeats while
|
||||
under `holdMs`), then — with more than one message — fades out (`fadeOutMs`), waits `gapMs`, and the
|
||||
next fades in. The fade (opacity on a `.fadewrap` layer) is independent of the scroll (the inner
|
||||
track's transform), so a long, scrolling message still fades at both ends. A lone message stays put
|
||||
(a long one keeps scrolling). A **viewport size change** (e.g. a portrait↔landscape rotation)
|
||||
re-measures the current message (`remeasureBanner`, debounced), so its scroll re-evaluates for the
|
||||
new width. All timings are operator-set (`/_gm/banner-settings`). Under **reduce-motion** the fades
|
||||
collapse to an instant swap
|
||||
and a long message does not scroll.
|
||||
|
||||
## Result / status iconography (`lib/result.ts`)
|
||||
|
||||
Reference in New Issue
Block a user