From e4830ad73f7391dc28e487352ab4b534bd611e89 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Tue, 14 Jul 2026 13:23:44 +0200 Subject: [PATCH] feat(frontend): drive seek bar with rAF for smooth playback progress --- .../src/lib/components/player/SeekBar.svelte | 4 +- frontend/src/lib/player/player.svelte.ts | 38 ++++++++++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/components/player/SeekBar.svelte b/frontend/src/lib/components/player/SeekBar.svelte index deebf69..1b5fcc5 100644 --- a/frontend/src/lib/components/player/SeekBar.svelte +++ b/frontend/src/lib/components/player/SeekBar.svelte @@ -19,11 +19,11 @@ class="group relative h-1.5 flex-1 cursor-pointer rounded-full bg-[var(--mf-track)] transition-[height] hover:h-2" >
diff --git a/frontend/src/lib/player/player.svelte.ts b/frontend/src/lib/player/player.svelte.ts index 0601fe7..5e701fc 100644 --- a/frontend/src/lib/player/player.svelte.ts +++ b/frontend/src/lib/player/player.svelte.ts @@ -61,6 +61,7 @@ class PlayerState { #audio: HTMLAudioElement | null = null; #loadToken = 0; #accentCache = new Map(); + #rafId: number | null = null; current = $derived(this.tracks.find((t) => t.id === this.currentId) ?? EMPTY); accent = $derived(this.accentColor ?? accentForHue(this.current.hue)); @@ -79,15 +80,22 @@ class PlayerState { const audio = new Audio(); audio.preload = 'auto'; audio.volume = this.volume / 100; - audio.addEventListener('timeupdate', () => { - this.progress = audio.currentTime; - }); audio.addEventListener('durationchange', () => this.#syncDuration(audio.duration)); audio.addEventListener('loadedmetadata', () => this.#syncDuration(audio.duration)); - audio.addEventListener('play', () => (this.playing = true)); - audio.addEventListener('pause', () => (this.playing = false)); - audio.addEventListener('ended', () => this.next()); + audio.addEventListener('play', () => { + this.playing = true; + this.#startProgressLoop(); + }); + audio.addEventListener('pause', () => { + this.playing = false; + this.#stopProgressLoop(); + }); + audio.addEventListener('ended', () => { + this.#stopProgressLoop(); + this.next(); + }); audio.addEventListener('error', () => { + this.#stopProgressLoop(); this.loading = false; this.playing = false; this.error = 'No se pudo reproducir la pista.'; @@ -96,6 +104,24 @@ class PlayerState { return audio; } + #tickProgress = () => { + const audio = this.#audio; + if (!audio) return; + this.progress = audio.currentTime; + this.#rafId = requestAnimationFrame(this.#tickProgress); + }; + + #startProgressLoop() { + if (this.#rafId !== null) return; + this.#rafId = requestAnimationFrame(this.#tickProgress); + } + + #stopProgressLoop() { + if (this.#rafId === null) return; + cancelAnimationFrame(this.#rafId); + this.#rafId = null; + } + #syncDuration(value: number) { if (!Number.isFinite(value) || value <= 0) return; const id = this.currentId;