improve: scroll to current lyric in real time while adjusting progress
This commit is contained in:
parent
00549a504c
commit
e1aa87ece0
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aquavox",
|
"name": "aquavox",
|
||||||
"version": "1.10.1",
|
"version": "1.12.4",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
let nextUpdate = -1;
|
let nextUpdate = -1;
|
||||||
let lastAdjustProgress = 0;
|
let lastAdjustProgress = 0;
|
||||||
let localProgress = 0;
|
let localProgress = 0;
|
||||||
|
let lastScroll = 0;
|
||||||
|
let scrolling = false;
|
||||||
|
let scriptScrolling = false;
|
||||||
|
|
||||||
let refs: HTMLParagraphElement[] = [];
|
let refs: HTMLParagraphElement[] = [];
|
||||||
let _refs: any[] = [];
|
let _refs: any[] = [];
|
||||||
@ -30,8 +33,19 @@
|
|||||||
else return 'previous-lyric';
|
else return 'previous-lyric';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scroll to correspoding lyric while adjusting progress
|
||||||
$: {
|
$: {
|
||||||
if (lyricsContainer && originalLyrics && originalLyrics.scripts) {
|
if ($userAdjustingProgress == true) {
|
||||||
|
const currentLyric = refs[getLyricIndex(progress)];
|
||||||
|
scrollToLyric(currentLyric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
(() => {
|
||||||
|
if (lyricsContainer === undefined || originalLyrics.scripts === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const scripts = originalLyrics.scripts;
|
const scripts = originalLyrics.scripts;
|
||||||
currentPositionIndex = getLyricIndex(progress);
|
currentPositionIndex = getLyricIndex(progress);
|
||||||
const cl = scripts[currentPositionIndex];
|
const cl = scripts[currentPositionIndex];
|
||||||
@ -42,7 +56,10 @@
|
|||||||
currentLyricIndex = -1;
|
currentLyricIndex = -1;
|
||||||
nextUpdate = cl.start;
|
nextUpdate = cl.start;
|
||||||
}
|
}
|
||||||
if ($userAdjustingProgress === false) {
|
const currentLyric = refs[currentPositionIndex];
|
||||||
|
if ($userAdjustingProgress === true || scrolling || currentLyric.getBoundingClientRect().top < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (let i = 0; i < scripts.length; i++) {
|
for (let i = 0; i < scripts.length; i++) {
|
||||||
const offset = Math.abs(i - currentPositionIndex);
|
const offset = Math.abs(i - currentPositionIndex);
|
||||||
const blurRadius = Math.min(offset * 1.5, 16);
|
const blurRadius = Math.min(offset * 1.5, 16);
|
||||||
@ -50,15 +67,14 @@
|
|||||||
refs[i].style.filter = `blur(${blurRadius}px)`;
|
refs[i].style.filter = `blur(${blurRadius}px)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sleep(ms: number) {
|
function sleep(ms: number) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function a(h: number) {
|
async function moveToNextLine(h: number) {
|
||||||
let pos = currentPositionIndex + 2;
|
let pos = currentPositionIndex + 2;
|
||||||
for (let i = currentPositionIndex + 2; i < refs.length; i++) {
|
for (let i = currentPositionIndex + 2; i < refs.length; i++) {
|
||||||
const lyric = refs[i];
|
const lyric = refs[i];
|
||||||
@ -96,12 +112,20 @@
|
|||||||
for (let i = 0; i < refs.length; i++) {
|
for (let i = 0; i < refs.length; i++) {
|
||||||
refs[i].style.transform = `translateY(0px)`;
|
refs[i].style.transform = `translateY(0px)`;
|
||||||
}
|
}
|
||||||
|
scriptScrolling = true;
|
||||||
lyricsContainer.scrollTop += h;
|
lyricsContainer.scrollTop += h;
|
||||||
|
setTimeout(() => {
|
||||||
|
scriptScrolling = false;
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function b(currentLyric: HTMLParagraphElement) {
|
async function scrollToLyric(currentLyric: HTMLParagraphElement) {
|
||||||
if (!originalLyrics || !originalLyrics.scripts || !lyricsContainer) return;
|
if (!originalLyrics || !originalLyrics.scripts || !lyricsContainer) return;
|
||||||
|
scriptScrolling = true;
|
||||||
lyricsContainer.scrollTop += currentLyric.getBoundingClientRect().top - 144;
|
lyricsContainer.scrollTop += currentLyric.getBoundingClientRect().top - 144;
|
||||||
|
setTimeout(() => {
|
||||||
|
scriptScrolling = false;
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
userAdjustingProgress.subscribe((v) => {
|
userAdjustingProgress.subscribe((v) => {
|
||||||
@ -126,9 +150,10 @@
|
|||||||
// progressBarRaw is used to detect progress changes at system-level (not in AquaVox)
|
// progressBarRaw is used to detect progress changes at system-level (not in AquaVox)
|
||||||
progressBarRaw.subscribe((progress: number) => {
|
progressBarRaw.subscribe((progress: number) => {
|
||||||
if ($userAdjustingProgress === false && getLyricIndex) {
|
if ($userAdjustingProgress === false && getLyricIndex) {
|
||||||
|
// prevent calling too frequent
|
||||||
if (Math.abs(localProgress - progress) > 0.6) {
|
if (Math.abs(localProgress - progress) > 0.6) {
|
||||||
const currentLyric = refs[getLyricIndex(progress)];
|
const currentLyric = refs[getLyricIndex(progress)];
|
||||||
b(currentLyric);
|
scrollToLyric(currentLyric);
|
||||||
}
|
}
|
||||||
localProgress = progress;
|
localProgress = progress;
|
||||||
}
|
}
|
||||||
@ -141,21 +166,52 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
|
(() => {
|
||||||
if ($userAdjustingProgress) {
|
if ($userAdjustingProgress) {
|
||||||
nextUpdate = progress;
|
nextUpdate = progress;
|
||||||
} else {
|
return;
|
||||||
if (nextUpdate - progress < 0.05) {
|
}
|
||||||
|
|
||||||
|
if (nextUpdate - progress >= 0.05) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
currentPositionIndex >= 0 &&
|
currentPositionIndex < 0 ||
|
||||||
currentPositionIndex !== currentAnimationIndex &&
|
currentPositionIndex === currentAnimationIndex ||
|
||||||
currentPositionIndex !== lastAdjustProgress
|
currentPositionIndex === lastAdjustProgress ||
|
||||||
|
scrolling
|
||||||
) {
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentLyric = refs[currentPositionIndex];
|
||||||
|
|
||||||
|
if (originalLyrics.scripts && currentLyric.getBoundingClientRect().top < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const offsetHeight =
|
const offsetHeight =
|
||||||
refs[currentPositionIndex].getBoundingClientRect().height +
|
refs[currentPositionIndex].getBoundingClientRect().height +
|
||||||
refs[currentPositionIndex].getBoundingClientRect().top -
|
refs[currentPositionIndex].getBoundingClientRect().top -
|
||||||
144;
|
144;
|
||||||
const currentLyric = refs[currentPositionIndex];
|
|
||||||
currentLyric.style.transition =
|
currentLyric.style.transition =
|
||||||
'transform .6s cubic-bezier(.28,.01,.29,.99), filter 200ms ease, opacity 200ms ease, font-size 200ms ease, scale 250ms ease';
|
'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)`;
|
currentLyric.style.transform = `translateY(${-offsetHeight}px)`;
|
||||||
@ -171,12 +227,10 @@
|
|||||||
nextLyric.style.transition =
|
nextLyric.style.transition =
|
||||||
'transform .6s cubic-bezier(.28,.01,.29,.99), filter 200ms ease, opacity 200ms ease, font-size 200ms ease, scale 250ms ease';
|
'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)`;
|
nextLyric.style.transform = `translateY(${-offsetHeight}px)`;
|
||||||
a(offsetHeight);
|
moveToNextLine(offsetHeight);
|
||||||
}
|
}
|
||||||
currentAnimationIndex = currentPositionIndex;
|
currentAnimationIndex = currentPositionIndex;
|
||||||
}
|
})();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -185,6 +239,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
|
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"
|
text-left no-scrollbar overflow-y-auto z-[1] pt-16 lyrics"
|
||||||
bind:this={lyricsContainer}
|
bind:this={lyricsContainer}
|
||||||
|
on:scroll={scrollHandler}
|
||||||
>
|
>
|
||||||
{#if debugMode}
|
{#if debugMode}
|
||||||
<p class="fixed top-6 right-20 font-mono text-sm">
|
<p class="fixed top-6 right-20 font-mono text-sm">
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
AquaVox 1.10.1 · 早期公开预览 · 源代码参见
|
AquaVox 1.12.4 · 早期公开预览 · 源代码参见
|
||||||
<a href="https://github.com/alikia2x/aquavox">GitHub</a>
|
<a href="https://github.com/alikia2x/aquavox">GitHub</a>
|
||||||
</p>
|
</p>
|
||||||
<a href="/import">导入音乐</a> <br />
|
<a href="/import">导入音乐</a> <br />
|
||||||
|
@ -72,13 +72,13 @@
|
|||||||
>
|
>
|
||||||
{song.singer.join(', ')}
|
{song.singer.join(', ')}
|
||||||
</span>
|
</span>
|
||||||
<div class="absolute right-2 bottom-2 text-right">
|
<div class="absolute right-2 bottom-2 text-right text-white" style="text-shadow: 0px 0px 4px rgba(65, 65, 65, .6);">
|
||||||
{#if song.duration}
|
{#if song.duration}
|
||||||
<span style="text-shadow: 0px 0px 4px rgba(65, 65, 65, .6);">{formatDuration(song.duration)}</span>
|
<span>{formatDuration(song.duration)}</span>
|
||||||
{/if}
|
{/if}
|
||||||
<br />
|
<br />
|
||||||
{#if song.views}
|
{#if song.views}
|
||||||
<span style="text-shadow: 0px 0px 4px rgba(65, 65, 65, .6);">{formatViews(song.views)}播放</span>
|
<span>{formatViews(song.views)}播放</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user