web: node fade-in spans the whole round, playback never sits still
Per Justin: instead of a 250 ms pop followed by a still frame, each newly reached node now fades and scales in across the full round interval (scaled with playback speed, staggered per node), so the animation reads as continuous motion like the prototype gif. Reduced motion still collapses to discrete swaps.
This commit is contained in:
parent
85520ca504
commit
e9d57b2082
1 changed files with 20 additions and 11 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { layoutForGraph, NETWORK_VIEW_HEIGHT, NETWORK_VIEW_WIDTH } from '@/composables/useLayout'
|
import { layoutForGraph, NETWORK_VIEW_HEIGHT, NETWORK_VIEW_WIDTH } from '@/composables/useLayout'
|
||||||
|
import { ROUND_MS } from '@/composables/usePlayback'
|
||||||
import { useSimStore } from '@/composables/useSimStore'
|
import { useSimStore } from '@/composables/useSimStore'
|
||||||
import { graphKey } from '@/lib/graph'
|
import { graphKey } from '@/lib/graph'
|
||||||
import type { PanelSpec } from '@/presets/types'
|
import type { PanelSpec } from '@/presets/types'
|
||||||
|
|
@ -50,16 +51,24 @@ function edgeEnd(edge: number[], side: 0 | 1) {
|
||||||
return layout.value[edge[side] ?? 0] ?? { x: 0, y: 0 }
|
return layout.value[edge[side] ?? 0] ?? { x: 0, y: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
// A small deterministic stagger inside the round so the pops read as a
|
// The fade-in spans the whole round interval (scaled with playback speed)
|
||||||
// wave instead of one synchronized blink. Stays well inside the 700 ms
|
// and each node starts at a small deterministic offset, so during playback
|
||||||
// round cadence (250 ms pop + at most 150 ms delay).
|
// something is always in motion; there is never a finished still frame
|
||||||
|
// between rounds.
|
||||||
|
const popDurationMs = computed(() => ROUND_MS / store.state.speed)
|
||||||
|
|
||||||
function popDelay(nodeIndex: number): string {
|
function popDelay(nodeIndex: number): string {
|
||||||
return `${(nodeIndex * 37) % 150}ms`
|
return `${((nodeIndex * 37) % 150) / store.state.speed}ms`
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg class="net" :viewBox="`0 0 ${NETWORK_VIEW_WIDTH} ${NETWORK_VIEW_HEIGHT}`" aria-hidden="true">
|
<svg
|
||||||
|
class="net"
|
||||||
|
:viewBox="`0 0 ${NETWORK_VIEW_WIDTH} ${NETWORK_VIEW_HEIGHT}`"
|
||||||
|
:style="{ '--pop-ms': `${popDurationMs}ms` }"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
<line
|
<line
|
||||||
v-for="(edge, edgeIndex) in edges"
|
v-for="(edge, edgeIndex) in edges"
|
||||||
:key="edgeIndex"
|
:key="edgeIndex"
|
||||||
|
|
@ -116,19 +125,19 @@ function popDelay(nodeIndex: number): string {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nodes reached this round pop in: scale 0.6 to 1.0 plus fade, 250 ms
|
/* Nodes reached this round fade and scale in across the full round
|
||||||
ease-out (spec 5.4). The global reduced-motion rule collapses this to a
|
interval (driven by --pop-ms), so playback reads as continuous motion.
|
||||||
discrete swap. */
|
The global reduced-motion rule collapses this to a discrete swap. */
|
||||||
.pop {
|
.pop {
|
||||||
animation: pop 250ms ease-out backwards;
|
animation: pop var(--pop-ms, 650ms) ease-out backwards;
|
||||||
transform-box: fill-box;
|
transform-box: fill-box;
|
||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pop {
|
@keyframes pop {
|
||||||
from {
|
from {
|
||||||
transform: scale(0.6);
|
transform: scale(0.5);
|
||||||
opacity: 0.2;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue