web: dots trickle in randomly per round, the share counts live

Per Justin: a round's dots no longer start fading together. Every node
gets a deterministic random appearance moment inside the first 70% of
the round interval (the rest is its fade), and a new roundProgress
value, driven by the playback frame loop, feeds the same jitter into
the panel stats: the big percentage and reached count now tick up
exactly as dots start appearing, in the cards, the mobile strip, and
the focus modal. Pausing, stepping, and seeking hold the full round;
reduced motion keeps discrete swaps.
This commit is contained in:
Justin Visser 2026-06-10 18:14:35 +02:00
parent 609ba6f0dd
commit 3cd48ca7ea
9 changed files with 342 additions and 16 deletions

View file

@ -5,7 +5,7 @@ import PanelEditorPopover from './PanelEditorPopover.vue'
import PanelMenu, { type PanelMenuItem } from './PanelMenu.vue'
import { useSimStore } from '@/composables/useSimStore'
import { roundPct } from '@/lib/format'
import { reachedCountAtRound } from '@/lib/reach'
import { reachedCountDisplayed } from '@/lib/reach'
import { MAX_PANELS, type PanelSpec } from '@/presets/types'
const props = defineProps<{ panel: PanelSpec; accent: string }>()
@ -61,11 +61,14 @@ function closeEditor() {
const result = computed(() => store.state.resultsByPanelId[props.panel.id])
const numStudents = computed(() => store.effectiveConfig(props.panel).numStudents)
// The numbers follow the playhead: they count what the network picture
// shows at the current round, reaching the final outcome on the last one.
// The numbers follow the playhead live: they count exactly the dots that
// have started appearing mid-transition, reaching the final outcome on
// the last round.
const reachedCount = computed(() => {
const panelResult = result.value
return panelResult ? reachedCountAtRound(panelResult.reachedAtRound, store.state.round) : 0
return panelResult
? reachedCountDisplayed(panelResult.reachedAtRound, store.state.round, store.state.roundProgress)
: 0
})
const reachedPctNow = computed(() => (reachedCount.value / numStudents.value) * 100)
const tone = computed(() =>