feature: volume persistent, better lyric scroll

This commit is contained in:
Alikia2x 2024-05-17 01:22:50 +08:00
parent 1cd5892ffe
commit 61bb6654d5
6 changed files with 130 additions and 22 deletions

View File

@ -3,7 +3,7 @@
"tabWidth": 4,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"printWidth": 120,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

View File

@ -1,6 +1,6 @@
{
"name": "aquavox",
"version": "1.8.3",
"version": "1.9.0",
"private": false,
"scripts": {
"dev": "vite dev",

View File

@ -34,6 +34,7 @@
function volumeBarOnChange(e: any) {
adjustVolume(e.target.value);
localStorage.setItem('volume', e.target.value);
}
onMount(() => {
@ -43,9 +44,7 @@
});
$: {
console.log(songInfoTopContainer, songInfoTopContent);
if (songInfoTopContainer && songInfoTopContent) {
console.log(songInfoTopContent.offsetWidth, songInfoTopContainer.offsetWidth);
isInfoTopOverflowing =
songInfoTopContent.offsetWidth > songInfoTopContainer.offsetWidth;
}
@ -77,7 +76,7 @@
bind:this={songInfoTopContent}>{name}</span
>
</div>
<span class="song-author">{singer}</span>
<span class="song-author text-shadow-lg">{singer}</span>
</div>
{/if}

View File

@ -3,23 +3,75 @@
export let lyrics: string[];
export let originalLyrics: Line[];
export let progress: number;
function userSlideProgress() {
systemCouldScrollSince = 0;
};
export { userSlideProgress };
let currentScrollPos = '';
let currentLyric: Line;
let currentLyricIndex = -1;
let lyricsContainer: HTMLDivElement;
let systemScrolling = false;
let systemCouldScrollSince = 0;
let lastScroll = 0;
let refs = [];
let _refs: any[] = [];
$: refs = _refs.filter(Boolean);
function smoothScrollTo(element: HTMLElement, to: number, duration: number, timingFunction: Function) {
if (systemCouldScrollSince > Date.now()) return;
systemScrolling = true;
const start = element.scrollTop;
const change = to - start;
const startTime = performance.now();
function animateScroll(timestamp: number) {
const elapsedTime = timestamp - startTime;
const progress = Math.min(elapsedTime / duration, 1);
const easedProgress = timingFunction(progress, 1.1, 0, 1, 1);
element.scrollTop = start + change * easedProgress;
console.log(elapsedTime);
if (elapsedTime < duration) {
requestAnimationFrame(animateScroll);
} else {
console.log('?');
systemScrolling = false;
}
}
requestAnimationFrame(animateScroll);
}
// Define your custom Bézier curve function
function customBezier(progress: number, p1x: number, p1y: number, p2x: number, p2y: number) {
function cubicBezier(t: number, p1: number, p2: number) {
const c = 3 * p1;
const b = 3 * (p2 - p1) - c;
const a = 1 - c - b;
return a * Math.pow(t, 3) + b * Math.pow(t, 2) + c * t;
}
return cubicBezier(progress, p1x, p2x);
}
function getClass(lyricIndex: number, progress: number) {
if (!currentLyric) return 'after-lyric';
if (lyricIndex === currentLyricIndex) return 'current-lyric';
else if (progress > currentLyric.endSeconds) return 'after-lyric';
else return 'previous-lyric';
}
function inRange(x: number, min: number, max: number) {
return (x - min) * (x - max) <= 0;
}
$: {
if (originalLyrics) {
if (originalLyrics && lyricsContainer) {
let found = false;
let finallyFound = false;
for (let i = 0; i < originalLyrics.length; i++) {
let l = originalLyrics[i];
if (progress >= l.startSeconds && progress <= l.endSeconds) {
@ -28,12 +80,44 @@
found = true;
const currentRef = refs[i];
if (currentRef && currentScrollPos !== currentLyric.text) {
currentRef.scrollIntoView({ behavior: 'smooth', block: 'center' });
const targetScroll = lyricsContainer.scrollTop + currentRef.getBoundingClientRect().top - 320;
const duration = 700;
smoothScrollTo(lyricsContainer, targetScroll, duration, customBezier);
lastScroll = 0;
currentScrollPos = currentLyric.text;
}
break;
}
}
if (!found) {
for (let i = 0; i < originalLyrics.length; i++) {
let l = originalLyrics[i];
let nl = i + 1 < originalLyrics.length ? originalLyrics[i + 1] : originalLyrics[i];
if (
progress >= l.endSeconds &&
progress < nl.startSeconds &&
inRange(lastScroll, l.endSeconds, nl.startSeconds) === false
) {
const currentRef = refs[i];
const targetScroll = lyricsContainer.scrollTop + currentRef.getBoundingClientRect().top - 320;
const duration = 700;
currentLyricIndex = i - 0.1;
currentLyric = {
id: '-1',
startTime: '00:00:00,000',
startSeconds: l.endSeconds + 0.01,
endTime: '00:00:00,000',
endSeconds: nl.startSeconds - 0.01,
text: ''
};
smoothScrollTo(lyricsContainer, targetScroll, duration, customBezier);
lastScroll = progress;
finallyFound = true;
break;
}
}
}
if (systemCouldScrollSince < Date.now()) {
for (let i = 0; i < lyrics.length; i++) {
const offset = Math.abs(i - currentLyricIndex);
const blurRadius = Math.min(offset * 1, 16);
@ -41,7 +125,8 @@
refs[i].style.filter = `blur(${blurRadius}px)`;
}
}
if (!found) {
}
if (found == false && finallyFound == false) {
currentLyric = {
id: '-1',
startTime: '00:00:00,000',
@ -58,7 +143,17 @@
{#if lyrics && originalLyrics}
<div
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 scroll-smooth no-scrollbar overflow-y-auto z-[1] lyrics"
text-left no-scrollbar overflow-y-auto z-[1] lyrics py-16"
bind:this={lyricsContainer}
on:scroll={(e) => {
if (systemScrolling == false) {
console.log('yes');
for (let i = 0; i < lyrics.length; i++) {
refs[i].style.filter = `blur(0px)`;
}
systemCouldScrollSince = Date.now() + 5000;
}
}}
>
{#each lyrics as lyric, i}
<p bind:this={_refs[i]} class={getClass(i, progress)}>
@ -70,7 +165,12 @@
<style>
.lyrics {
mask-image: linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,1) 2rem, rgba(0,0,0,1) calc(100% - 5rem), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 1) 2rem,
rgba(0, 0, 0, 1) calc(100% - 5rem),
rgba(0, 0, 0, 0) 100%
);
}
.no-scrollbar {
scrollbar-width: none;
@ -92,7 +192,7 @@
position: relative;
color: rgba(255, 255, 255, 0.7);
font-weight: 600;
font-size: 2.2rem;
font-size: 2.3rem;
line-height: 2.7rem;
filter: blur(1px);
top: 1rem;
@ -103,7 +203,7 @@
position: relative;
color: rgba(255, 255, 255, 0.7);
font-weight: 600;
font-size: 2.2rem;
font-size: 2.3rem;
line-height: 2.7rem;
filter: blur(1px);
top: 1rem;
@ -117,12 +217,12 @@
margin: 2.4rem 0rem;
}
.after-lyric {
font-size: 2.8rem;
font-size: 3rem;
line-height: 3.3rem;
margin: 2.4rem 0rem;
}
.previous-lyric {
font-size: 2.8rem;
font-size: 3em;
line-height: 3.3rem;
margin: 2.4rem 0rem;
}

View File

@ -77,7 +77,7 @@
</ul>
</div>
<p>
AquaVox 1.8.3 · 早期公开预览 · 源代码参见
AquaVox 1.9.0 · 早期公开预览 · 源代码参见
<a href="https://github.com/alikia2x/aquavox">GitHub</a>
</p>
<a href="/import">导入音乐</a> <br />

View File

@ -27,6 +27,7 @@
let lyricsText: string[] = [];
let onAdjustingProgress = false;
let hasLyrics: boolean;
let lyricComp: any;
const coverPath = writable('');
let mainInterval: ReturnType<typeof setInterval>;
@ -134,12 +135,14 @@
if (audioPlayer) {
audioPlayer.currentTime = duration * progress;
currentProgress = duration * progress;
lyricComp.userSlideProgress();
}
}
function adjustRealProgress(progress: number) {
if (audioPlayer) {
currentProgress = duration * progress;
lyricComp.userSlideProgress();
}
}
@ -163,9 +166,15 @@
) {
currentProgress = audioPlayer.currentTime;
}
}, 100);
}, 17);
}
import { onMount } from 'svelte';
onMount(() => {
audioPlayer.volume = localStorage.getItem('volume') ? Number(localStorage.getItem('volume')) : 1;
});
$: {
if (audioPlayer) {
paused = audioPlayer.paused;
@ -206,7 +215,7 @@
{hasLyrics}
/>
<Lyrics lyrics={lyricsText} {originalLyrics} progress={currentProgress} />
<Lyrics lyrics={lyricsText} {originalLyrics} progress={currentProgress} bind:this={lyricComp}/>
<audio
bind:this={audioPlayer}