forked from Keelrleek/SoulStream
player bar: a complete waveform arrives as one motion instead of popping
Chosen on waveform-arrive.html (M2, direct morph, 1000ms): when a track has its whole waveform at once — a local file, a fully-cached stream, or the final decode replacing a stream's progressive peaks — every bar eases from whatever the lane currently shows straight into the new shape, all together. From the resting dots that reads as a uniform grow; from a mid-sweep swell or the previous track's bars it is literally one waveform transforming into another. The streaming rise keeps its left-to-right story untouched; the morph fires only on a peaks swap at coverage 1, and never on a resize, which re-lays the same shape and stays instant. The canvas keeps a record of what it actually painted last frame — dots, swell, rise, morph or settled — so the morph always starts from the truth on screen, and the fade colours (accent, boundary blend, hover wash) stay live through it. The rail mirrors the motion in CSS: an `arriving` class carries the same 1000ms house-ease height transition, placed after the rise rule so a stream's final decode arrives on this ease too. Measured live on a cached start: bar heights walked 5.2 → 8.2 → 10.6 → 12.3 → 13.6 → 14.6 → 15.0 and settled at 15.1 at the 1000ms mark — the ease-out curve, frame by frame.
This commit is contained in:
1 parent
4c9a6e312a
commit
1765de65cf
3 files changed
+119
-11
No files matched your search
@@ -37,6 +37,7 @@
|
||||
BAR_W,
|
||||
waveLevels,
|
||||
RISE_CADENCE_MS,
|
||||
ARRIVE_MS,
|
||||
PENDING_ALPHA,
|
||||
} from "../lib/waveshape.js";
|
||||
|
||||
@@ -398,6 +399,28 @@
|
||||
return wavePending || v == null || (railRising && i >= railRevealed);
|
||||
}
|
||||
|
||||
// The arrival morph — the playbar's M2, in CSS: when a COMPLETE waveform
|
||||
// lands (local track, cached stream, a stream's final decode), the
|
||||
// `arriving` class puts an ARRIVE_MS height transition on every bar, so
|
||||
// the whole lane eases from whatever it showed into the new shape at
|
||||
// once. Svelte reuses the <i> nodes across the swap, which is what makes
|
||||
// the transition run from the old inline heights.
|
||||
let railArriving = $state(false);
|
||||
let railArrivePeaks = null;
|
||||
let railArriveTimer = null;
|
||||
$effect(() => {
|
||||
const w = player.waveform;
|
||||
if (w.length > 0 && railCoverage >= 1 && w !== railArrivePeaks) {
|
||||
railArrivePeaks = w;
|
||||
railArriving = true;
|
||||
clearTimeout(railArriveTimer);
|
||||
railArriveTimer = setTimeout(() => (railArriving = false), ARRIVE_MS + 80);
|
||||
} else if (w.length === 0) {
|
||||
railArrivePeaks = null;
|
||||
}
|
||||
});
|
||||
$effect(() => () => clearTimeout(railArriveTimer));
|
||||
|
||||
/* `bars` is already shaped 0..1 by waveshape.js (which also explains, with
|
||||
measurements, why the old gain-and-clamp rule here rendered half the
|
||||
lane at one height and had to be propped up with per-bar noise). This
|
||||
@@ -1082,6 +1105,7 @@
|
||||
class:pending={wavePending}
|
||||
class:loading={wavePending && player.wavePending}
|
||||
class:rising={railRising}
|
||||
class:arriving={railArriving}
|
||||
style="--bar-gap: {BAR_PITCH - BAR_W}px; --bars: {waveBars.length}"
|
||||
role="slider"
|
||||
tabindex="0"
|
||||
@@ -1978,6 +2002,15 @@
|
||||
height 2000ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
opacity 400ms linear;
|
||||
}
|
||||
/* Arrival morph — the playbar's M2: a complete waveform eases in as one
|
||||
motion, every bar at once. 1000ms is ARRIVE_MS in waveshape.js — change
|
||||
them together. After `.rising` so a stream's final decode arrives on
|
||||
this ease even while the rise class lingers. */
|
||||
.wave.arriving i {
|
||||
transition:
|
||||
height 1000ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
opacity 400ms linear;
|
||||
}
|
||||
.times {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Reference in new issue
Block a user