From c83447ebd2e25d364d8224c4faa76fc0c4106827 Mon Sep 17 00:00:00 2001 From: cucadmuh <49571317+cucadmuh@users.noreply.github.com> Date: Thu, 7 May 2026 00:48:06 +0300 Subject: [PATCH] fix(player): reset WaveformSeek interpolation anchor on resume (#486) The interpolation effect unmounts while paused, so progressAnchorRef.atMs was never refreshed. The first tick after play added the entire pause duration to elapsedSec and overshot the playhead until the next transport heartbeat. Re-anchor from getPlaybackProgressSnapshot() when the effect starts. --- src/components/WaveformSeek.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/components/WaveformSeek.tsx b/src/components/WaveformSeek.tsx index 7798ce9c..882e77cb 100644 --- a/src/components/WaveformSeek.tsx +++ b/src/components/WaveformSeek.tsx @@ -1212,6 +1212,22 @@ export default function WaveformSeek({ trackId }: Props) { useEffect(() => { if (!isPlaying || previewFreezesMainSeekbar || duration <= 0 || !isFinite(duration)) return; + // This effect is torn down while paused, so `progressAnchorRef.atMs` is not refreshed. + // On resume the first `tick` would add the entire pause duration to `elapsedSec` and + // overshoot the playhead until the next transport heartbeat corrects it. + const snap = getPlaybackProgressSnapshot(); + const raw = snap.progress; + progressRef.current = raw; + progressAnchorRef.current = { + progress: raw, + atMs: performance.now(), + }; + const resumeVisual = isBarQuantizedSeekStyle(styleRef.current) + ? quantizeProgressByBars(raw) + : raw; + visualTargetProgressRef.current = resumeVisual; + visualProgressRef.current = resumeVisual; + let rafId: number | null = null; let lastPaintAt = 0; const tick = (now: number) => {