web: playback, slice 4 of M3
usePlayback drives one global round on a rAF timer: 700 ms per round at 1x, speed pill cycling 0.5/1/2, play-once autoplay after the first load, no looping; at the final round the play button becomes replay. Under prefers-reduced-motion there is no autoplay, the page settles on the final round, and the cadence slows to 1000 ms. The PlayerBar is the mockup's centered pill with a native range scrubber (one tick per round, aria-valuetext, seeking pauses) and the global keyboard map: Space toggles, arrows step, Home/End jump; text inputs and native control handling are left alone. A single polite live region announces run updates, pauses, scrub releases, and the final state. Nodes reached in the current round pop in (scale 0.6 to 1, 250 ms ease-out) with a small deterministic stagger so each round reads as a wave. Per Justin's feedback the panel percentage and reached count now follow the playhead instead of sitting on the final outcome; the hero keeps the final numbers, since its sentence claims outcomes.
This commit is contained in:
parent
71baf10e73
commit
0e3a14a0b3
7 changed files with 736 additions and 13 deletions
|
|
@ -6,16 +6,20 @@ import FooterDisclaimer from '@/components/FooterDisclaimer.vue'
|
|||
import HeroHeadline from '@/components/HeroHeadline.vue'
|
||||
import LegendRow from '@/components/LegendRow.vue'
|
||||
import PanelGrid from '@/components/PanelGrid.vue'
|
||||
import PlayerBar from '@/components/PlayerBar.vue'
|
||||
import ResultsTable from '@/components/ResultsTable.vue'
|
||||
import ScenarioToolbar from '@/components/ScenarioToolbar.vue'
|
||||
import { usePlayback } from '@/composables/usePlayback'
|
||||
import { useSimStore } from '@/composables/useSimStore'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
|
||||
const store = useSimStore()
|
||||
const playback = usePlayback()
|
||||
useTheme() // resolve and apply the theme before first paint of the app
|
||||
|
||||
onMounted(() => {
|
||||
void store.initialize()
|
||||
onMounted(async () => {
|
||||
await store.initialize()
|
||||
playback.autoplayOnce()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -27,6 +31,7 @@ onMounted(() => {
|
|||
<ErrorBanner />
|
||||
<PanelGrid />
|
||||
<LegendRow />
|
||||
<PlayerBar />
|
||||
<ResultsTable
|
||||
:panels="store.state.panels"
|
||||
:results-by-panel-id="store.state.resultsByPanelId"
|
||||
|
|
@ -34,6 +39,7 @@ onMounted(() => {
|
|||
/>
|
||||
</main>
|
||||
<FooterDisclaimer />
|
||||
<div class="visually-hidden" aria-live="polite">{{ store.state.announcement }}</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue