From bb27df5c8dea6986c99989d10a81afebd88aab51 Mon Sep 17 00:00:00 2001 From: alikia2x Date: Thu, 25 Jul 2024 19:03:09 +0800 Subject: [PATCH] fix: prevent negative transform in lyrics update. ref: let the code in lyrics more structured add: global truncate function improve: small improvements in routes/play/[id] --- package.json | 2 +- src/lib/components/interactiveBox.svelte | 5 +- src/lib/components/lyrics.svelte | 234 ++++++++++++----------- src/lib/truncate.ts | 3 + src/routes/play/[id]/+page.svelte | 24 +-- 5 files changed, 133 insertions(+), 135 deletions(-) create mode 100644 src/lib/truncate.ts diff --git a/package.json b/package.json index 0342a7d..395bcc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aquavox", - "version": "1.12.13", + "version": "1.12.14", "private": false, "scripts": { "dev": "vite dev", diff --git a/src/lib/components/interactiveBox.svelte b/src/lib/components/interactiveBox.svelte index 0bbf0e5..1d55162 100644 --- a/src/lib/components/interactiveBox.svelte +++ b/src/lib/components/interactiveBox.svelte @@ -3,6 +3,7 @@ import { onMount } from 'svelte'; import userAdjustingProgress from '$lib/state/userAdjustingProgress'; import progressBarSlideValue from '$lib/state/progressBarSlideValue'; + import truncate from '$lib/truncate'; export let name: string; export let singer: string = ''; @@ -49,10 +50,6 @@ progressBarSlideValue.set((e.offsetX / progressBar.getBoundingClientRect().width) * duration); } - function truncate(value: number, min: number, max: number) { - return Math.min(Math.max(value, min), max); - } - function progressBarMouseUp(offsetX: number) { adjustDisplayProgress(offsetX / progressBar.getBoundingClientRect().width); } diff --git a/src/lib/components/lyrics.svelte b/src/lib/components/lyrics.svelte index 92cd78f..59135ca 100644 --- a/src/lib/components/lyrics.svelte +++ b/src/lib/components/lyrics.svelte @@ -5,6 +5,7 @@ import type { LrcJsonData } from 'lrc-parser-ts'; import progressBarSlideValue from '$lib/state/progressBarSlideValue'; import nextUpdate from '$lib/state/nextUpdate'; + import truncate from '$lib/truncate'; // Component input properties export let lyrics: string[]; @@ -38,6 +39,22 @@ $: refs = _refs.filter(Boolean); $: getLyricIndex = createLyricsSearcher(originalLyrics); + + // handle KeyDown event + function onKeyDown(e: KeyboardEvent) { + if (e.altKey && e.shiftKey && (e.metaKey || e.key === 'OS') && e.key === 'Enter') { + debugMode = !debugMode; + localStorage.setItem('debugMode', debugMode ? 'true' : 'false'); + } + } + + // using for debug mode + function extractTranslateValue(s: string): string | null { + const regex = /translateY\((-?\d*px)\)/; + let arr = regex.exec(s); + return arr==null ? null : arr[1]; + } + // Helper function to get CSS class for a lyric based on its index and progress function getClass(lyricIndex: number, progress: number) { if (!originalLyrics.scripts) return 'previous-lyric'; @@ -117,7 +134,110 @@ }, 500); } - // Handle user adjusting progress state changes + // Handle scroll events in the lyrics container + function scrollHandler() { + scrolling = !scriptScrolling; + if (scrolling && originalLyrics.scripts) { + lastScroll = new Date().getTime(); + for (let i = 0; i < originalLyrics.scripts.length; i++) { + if (refs[i]) { + refs[i].style.filter = 'blur(0px)'; + } + } + } + setTimeout(() => { + if (new Date().getTime() - lastScroll > 5000) { + scrolling = false; + } + }, 5500); + } + + // Utility function to create a sleep/delay + function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + // Scroll to corresponding lyric while adjusting progress + $: { + if ($userAdjustingProgress == true) { + const currentLyric = refs[getLyricIndex(progress)]; + scrollToLyric(currentLyric); + } + } + + // Update the current lyric and apply blur effect based on the progress + // worked in real-time. + $: { + (() => { + if (!lyricsContainer || !originalLyrics.scripts) return; + + const scripts = originalLyrics.scripts; + currentPositionIndex = getLyricIndex(progress); + const cl = scripts[currentPositionIndex]; + + if (cl.start <= progress && progress <= cl.end) { + currentLyricIndex = currentPositionIndex; + nextUpdate.set(cl.end); + } else { + currentLyricIndex = -1; + nextUpdate.set(cl.start); + } + + const currentLyric = refs[currentPositionIndex]; + if ($userAdjustingProgress || scrolling || currentLyric.getBoundingClientRect().top < 0) return; + + for (let i = 0; i < scripts.length; i++) { + const offset = Math.abs(i - currentPositionIndex); + const blurRadius = Math.min(offset * 0.96, 16); + if (refs[i]) { + refs[i].style.filter = `blur(${blurRadius}px)`; + } + } + })(); + } + + // Main function that control's lyrics update during playing + // triggered by nextUpdate's update + async function lyricsUpdate(){ + if ( + currentPositionIndex < 0 || + currentPositionIndex === currentAnimationIndex || + currentPositionIndex === lastAdjustProgress || + $userAdjustingProgress === true || + scrolling + ) return; + + const currentLyric = refs[currentPositionIndex]; + const currentLyricRect = currentLyric.getBoundingClientRect(); + + if (originalLyrics.scripts && currentLyricRect.top < 0) return; + + const offsetHeight = truncate(currentLyricRect.top - currentLyricTopMargin, 0, Infinity); + + // prepare current line + currentLyric.style.transition = `transform .6s cubic-bezier(.28,.01,.29,.99), filter 200ms ease, + opacity 200ms ease, font-size 200ms ease, scale 250ms ease`; + currentLyric.style.transform = `translateY(${-offsetHeight}px)`; + + for (let i = currentPositionIndex - 1; i >= 0; i--) { + refs[i].style.transition = `transform .6s cubic-bezier(.28,.01,.29,.99), filter 200ms ease, + opacity 200ms ease, font-size 200ms ease, scale 250ms ease`; + refs[i].style.transform = `translateY(${-offsetHeight}px)`; + } + if (currentPositionIndex + 1 < refs.length) { + const nextLyric = refs[currentPositionIndex + 1]; + nextLyric.style.transition = `transform .6s cubic-bezier(.28,.01,.29,.99), filter 200ms ease, + opacity 200ms ease, font-size 200ms ease, scale 250ms ease`; + nextLyric.style.transform = `translateY(${-offsetHeight}px)`; + await moveToNextLine(offsetHeight); + } + currentAnimationIndex = currentPositionIndex; + } + + + nextUpdate.subscribe(lyricsUpdate) + + // Process while user is adjusting progress userAdjustingProgress.subscribe((adjusting) => { if (!originalLyrics) return; const scripts = originalLyrics.scripts; @@ -155,117 +275,6 @@ } }); - // Handle scroll events in the lyrics container - function scrollHandler() { - scrolling = !scriptScrolling; - if (scrolling && originalLyrics.scripts) { - lastScroll = new Date().getTime(); - for (let i = 0; i < originalLyrics.scripts.length; i++) { - if (refs[i]) { - refs[i].style.filter = 'blur(0px)'; - } - } - } - setTimeout(() => { - if (new Date().getTime() - lastScroll > 5000) { - scrolling = false; - } - }, 5500); - } - - // Utility function to create a sleep/delay - function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - - // Scroll to corresponding lyric while adjusting progress - $: { - if ($userAdjustingProgress == true) { - const currentLyric = refs[getLyricIndex(progress)]; - scrollToLyric(currentLyric); - } - } - - // Update the current lyric and apply blur effect based on the progress - $: { - (() => { - if (!lyricsContainer || !originalLyrics.scripts) return; - - const scripts = originalLyrics.scripts; - currentPositionIndex = getLyricIndex(progress); - const cl = scripts[currentPositionIndex]; - - if (cl.start <= progress && progress <= cl.end) { - currentLyricIndex = currentPositionIndex; - nextUpdate.set(cl.end); - } else { - currentLyricIndex = -1; - nextUpdate.set(cl.start); - } - - const currentLyric = refs[currentPositionIndex]; - if ($userAdjustingProgress || scrolling || currentLyric.getBoundingClientRect().top < 0) return; - - for (let i = 0; i < scripts.length; i++) { - const offset = Math.abs(i - currentPositionIndex); - const blurRadius = Math.min(offset * 0.96, 16); - if (refs[i]) { - refs[i].style.filter = `blur(${blurRadius}px)`; - } - } - })(); - } - - nextUpdate.subscribe(async (nextUpdate) => { - if ( - currentPositionIndex < 0 || - currentPositionIndex === currentAnimationIndex || - currentPositionIndex === lastAdjustProgress || - $userAdjustingProgress === true || - scrolling - ) return; - - const currentLyric = refs[currentPositionIndex]; - - if (originalLyrics.scripts && currentLyric.getBoundingClientRect().top < 0) return; - - const offsetHeight = - refs[currentPositionIndex].getBoundingClientRect().top - - currentLyricTopMargin; - - // prepare current line - currentLyric.style.transition = `transform .6s cubic-bezier(.28,.01,.29,.99), filter 200ms ease, - opacity 200ms ease, font-size 200ms ease, scale 250ms ease`; - currentLyric.style.transform = `translateY(${-offsetHeight}px)`; - - for (let i = currentPositionIndex - 1; i >= 0; i--) { - refs[i].style.transition = `transform .6s cubic-bezier(.28,.01,.29,.99), filter 200ms ease, - opacity 200ms ease, font-size 200ms ease, scale 250ms ease`; - refs[i].style.transform = `translateY(${-offsetHeight}px)`; - } - if (currentPositionIndex + 1 < refs.length) { - const nextLyric = refs[currentPositionIndex + 1]; - nextLyric.style.transition = `transform .6s cubic-bezier(.28,.01,.29,.99), filter 200ms ease, - opacity 200ms ease, font-size 200ms ease, scale 250ms ease`; - nextLyric.style.transform = `translateY(${-offsetHeight}px)`; - await moveToNextLine(offsetHeight); - } - currentAnimationIndex = currentPositionIndex; - }) - - function onKeyDown(e: KeyboardEvent) { - if (e.altKey && e.shiftKey && (e.metaKey || e.key === 'OS') && e.key === 'Enter') { - debugMode = !debugMode; - localStorage.setItem('debugMode', debugMode ? 'true' : 'false'); - } - } - - function extractTranslateValue(s: string): string | null { - const regex = /translateY\((-?\d*px)\)/; - let arr = regex.exec(s); - return arr==null ? null : arr[1]; - } - @@ -290,6 +299,7 @@ class="absolute top-[6.5rem] md:top-36 xl:top-0 w-screen xl:w-[52vw] px-6 md:px-12 lg:px-[7.5rem] xl:left-[45vw] xl:px-[3vw] h-[calc(100vh-17rem)] xl:h-screen font-sans text-left no-scrollbar overflow-y-auto z-[1] pt-16 lyrics" bind:this={lyricsContainer} + on:scroll={scrollHandler} > {#each lyrics as lyric, i}

diff --git a/src/lib/truncate.ts b/src/lib/truncate.ts new file mode 100644 index 0000000..c10b4a4 --- /dev/null +++ b/src/lib/truncate.ts @@ -0,0 +1,3 @@ +export default function truncate(value: number, min: number, max: number) { + return Math.min(Math.max(value, min), max); +} \ No newline at end of file diff --git a/src/routes/play/[id]/+page.svelte b/src/routes/play/[id]/+page.svelte index 9c2c853..a68d275 100644 --- a/src/routes/play/[id]/+page.svelte +++ b/src/routes/play/[id]/+page.svelte @@ -11,6 +11,8 @@ import lrcParser, { type LrcJsonData } from 'lrc-parser-ts'; import userAdjustingProgress from '$lib/state/userAdjustingProgress'; import type { IAudioMetadata } from 'music-metadata-browser'; + import { onMount } from 'svelte'; + import progressBarRaw from '$lib/state/progressBarRaw'; const audioId = $page.params.id; let audioPlayer: HTMLAudioElement; @@ -150,20 +152,12 @@ $: { clearInterval(mainInterval); mainInterval = setInterval(() => { - if ( - audioPlayer !== null && - audioPlayer.currentTime !== undefined - ) { - if ($userAdjustingProgress === false) - currentProgress = audioPlayer.currentTime; - progressBarRaw.set(audioPlayer.currentTime); - } + if ($userAdjustingProgress === false) + currentProgress = audioPlayer.currentTime; + progressBarRaw.set(audioPlayer.currentTime); }, 50); } - import { onMount } from 'svelte'; - import progressBarRaw from '$lib/state/progressBarRaw'; - onMount(() => { audioPlayer.volume = localStorage.getItem('volume') ? Number(localStorage.getItem('volume')) : 1; }); @@ -175,13 +169,7 @@ } } - $: { - if (originalLyrics) { - hasLyrics = true; - } else { - hasLyrics = false; - } - } + $: hasLyrics = !!originalLyrics; readDB();