Compare commits

..

No commits in common. "main" and "2.3.1" have entirely different histories.
main ... 2.3.1

4 changed files with 147 additions and 40 deletions

View File

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

View File

@ -5,6 +5,7 @@
import type { Spring } from '$lib/graphics/spring/spring';
const viewportWidth = document.documentElement.clientWidth;
const scaleCurrentLine = viewportWidth > 640 ? 1.02 : 1.045 ;
export let line: ScriptItem;
export let index: number;
@ -17,6 +18,7 @@
let time = 0;
let positionX: number = 0;
let positionY: number = 0;
let scale = 1;
let opacity = 1;
let stopped = false;
let lastPosX: number | undefined = undefined;
@ -74,6 +76,7 @@
export const setCurrent = (isCurrent: boolean) => {
isCurrentLyric = isCurrent;
opacity = isCurrent ? 1 : 0.36;
scale = isCurrent ? scaleCurrentLine : 1;
};
export const setBlur = (blur: number) => {
@ -109,8 +112,8 @@
lastPosY = pos.y;
positionX = pos.x;
positionY = pos.y;
springX = createSpring(pos.x, pos.x, 0.114, 0.72);
springY = createSpring(pos.y, pos.y, 0.114, 0.72);
springX = createSpring(pos.x, pos.x, 0.126, 0.8);
springY = createSpring(pos.y, pos.y, 0.126, 0.8);
};
export const stop = () => {
@ -123,36 +126,31 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
style="transform: translate3d({positionX}px, {positionY}px, 0); transition-property: opacity, text-shadow;
transition-duration: 0.36s; transition-timing-function: ease-out; opacity: {opacity};
style="transform: translate3d({positionX}px, {positionY}px, 0); scale: {scale};
transition-property: scale, opacity; transition-duration: 0.5s; transition-timing-function: ease-in-out; opacity: {opacity};
transform-origin: center left;"
class="absolute z-50 w-full pr-12 lg:pr-16 cursor-default py-5"
bind:this={ref}
on:touchstart={() => {
clickMask.style.backgroundColor = 'rgba(255,255,255,.3)';
clickMask.style.backgroundColor = "rgba(255,255,255,.3)";
}}
on:touchend={() => {
clickMask.style.backgroundColor = 'transparent';
clickMask.style.backgroundColor = "transparent";
}}
on:click={() => {
lyricClick(index);
}}
>
<span
class="absolute w-[calc(100%-2.5rem)] lg:w-[calc(100%-3rem)] h-full
-translate-x-2 lg:-translate-x-5 -translate-y-5 rounded-lg duration-300 lg:hover:bg-[rgba(255,255,255,.15)]"
bind:this={clickMask}
>
<span class="absolute w-[calc(100%-2.5rem)] lg:w-[calc(100%-2.75rem)] h-full
-translate-x-2 lg:-translate-x-5 -translate-y-5 rounded-lg duration-300 lg:hover:bg-[rgba(255,255,255,.15)]" bind:this={clickMask}>
</span>
{#if debugMode}
<span class="text-lg absolute -translate-y-7">
{index}: duration: {(line.end - line.start).toFixed(3)}, {line.start.toFixed(3)}~{line.end.toFixed(3)}
</span>
{/if}
<span
class={`text-white text-[2rem] leading-9 lg:text-5xl lg:leading-[4rem] font-semibold text-shadow-lg mr-4
${isCurrentLyric ? 'text-glow' : ''}`}
>
<span class={`text-white text-[2rem] leading-9 lg:text-5xl lg:leading-[4rem] font-semibold text-shadow-lg mr-4`}>
{line.text}
</span>
{#if line.translation}
@ -162,13 +160,3 @@
</span>
{/if}
</div>
<style>
.text-glow {
text-shadow:
0 0 3px #ffffff2c,
0 0 6px #ffffff2c,
0 15px 30px rgba(0, 0, 0, 0.11),
0 5px 15px rgba(0, 0, 0, 0.08);
}
</style>

View File

@ -8,9 +8,8 @@
// constants
const viewportHeight = document.documentElement.clientHeight;
const viewportWidth = document.documentElement.clientWidth;
const marginY = viewportWidth > 640 ? 12 : 0 ;
const blurRatio = viewportWidth > 640 ? 1 : 1.4;
const currentLyrictTop = viewportWidth > 640 ? viewportHeight * 0.12 : viewportHeight * 0.05;
const marginY = viewportWidth > 640 ? 36 : 0 ;
const currentLyrictTop = viewportHeight * 0.02;
const deceleration = 0.95; // Velocity decay factor for inertia
const minVelocity = 0.1; // Minimum velocity to stop inertia
document.body.style.overflow = 'hidden';
@ -57,7 +56,7 @@
}
function initLyricTopList() {
let cumulativeHeight = currentLyrictTop;
let cumulativeHeight = 0;
for (let i = 0; i < lyricLines.length; i++) {
const c = lyricComponents[i];
lyricElements.push(c.getRef());
@ -77,17 +76,13 @@
for (let i = 0; i < lyricElements.length; i++) {
const currentLyricComponent = lyricComponents[i];
let delay = 0;
if (i < currentLyricIndex) {
if (i <= currentLyricIndex) {
delay = 0;
}
else if (i == currentLyricIndex) {
delay = 0.042;
}
else {
delay = Math.min(Math.min(currentLyricDuration, 0.6), 0.067 * (i - currentLyricIndex+1.2));
} else {
delay = 0.013 + Math.min(Math.min(currentLyricDuration, 0.1), 0.075 * (i - currentLyricIndex));
}
const offset = Math.abs(i - currentLyricIndex);
let blurRadius = Math.min(offset * blurRatio, 16);
let blurRadius = Math.min(offset * 1.7, 16);
currentLyricComponent.setBlur(blurRadius);
currentLyricComponent.update({ x: 0, y: lyricTopList[i] - relativeOrigin }, delay);
}
@ -115,7 +110,7 @@
currentLyricComponent.setY(currentY - deltaY);
}
scrolling = true;
if (scrollingTimeout) clearTimeout(scrollingTimeout);
//if (scrollingTimeout) clearTimeout(scrollingTimeout);
scrollingTimeout = setTimeout(() => {
scrolling = false;
}, 5000);
@ -186,7 +181,7 @@
console.log("computeLayout")
computeLayout();
}
if (Math.abs(lastProgress - progress) > 0.5) {
if (Math.abs(lastProgress - progress) > 0) {
scrolling = false;
}
if (lastProgress - progress > 0) {

View File

@ -0,0 +1,124 @@
<script lang="ts">
import { onDestroy } from 'svelte';
import {
LyricPlayer as CoreLyricPlayer,
type LyricLineMouseEvent,
type LyricLine,
type spring
} from '@applemusic-like-lyrics/core';
export let disabled: boolean = false;
export let playing: boolean = true;
export let alignAnchor: 'top' | 'bottom' | 'center' = 'center';
export let alignPosition: number = 0.5;
export let enableSpring: boolean = true;
export let enableBlur: boolean = true;
export let enableScale: boolean = true;
export let hidePassedLines: boolean = false;
export let lyricLines: LyricLine[] = [];
export let currentTime: number = 0;
export let linePosXSpringParams: Partial<spring.SpringParams> = {};
export let linePosYSpringParams: Partial<spring.SpringParams> = {};
export let lineScaleSpringParams: Partial<spring.SpringParams> = {};
export let bottomLine: Node = document.createElement('div');
export let onLyricLineClick: (line: LyricLineMouseEvent) => void = () => {};
export let onLyricLineContextMenu: (line: LyricLineMouseEvent) => void = () => {};
export let lyricPlayer: CoreLyricPlayer;
let className;
export { className as class };
let lyricsElement: HTMLDivElement | null;
let bottomLineElement: HTMLDivElement | null;
let animationCanceled = false;
let animationLastTime = -1;
let lineClickHandler: (e: Event) => void;
let contextMenuHandler: (e: Event) => void;
$: {
if (playing) {
lyricPlayer.resume();
} else {
lyricPlayer.pause();
}
}
const onFrame = (time: number) => {
if (animationCanceled) return;
if (animationLastTime === -1) {
animationLastTime = time;
}
lyricPlayer.update(time - animationLastTime);
animationLastTime = time;
requestAnimationFrame(onFrame);
};
const startAnimation = () => {
animationCanceled = false;
animationLastTime = -1;
requestAnimationFrame(onFrame);
};
const stopAnimation = () => {
animationCanceled = true;
};
$: {
if (!disabled) {
startAnimation();
} else {
stopAnimation();
}
}
$: {
if (lyricsElement) {
lyricsElement.appendChild(lyricPlayer.getElement());
}
if (bottomLineElement) {
lyricPlayer.getBottomLineElement().appendChild(bottomLine);
}
}
$: {
lyricPlayer.setAlignAnchor(alignAnchor);
lyricPlayer.setAlignPosition(alignPosition);
lyricPlayer.setEnableSpring(enableSpring);
lyricPlayer.setEnableScale(enableScale);
lyricPlayer.setEnableBlur(enableBlur);
lyricPlayer.setLinePosXSpringParams(linePosXSpringParams);
lyricPlayer.setLinePosYSpringParams(linePosYSpringParams);
lyricPlayer.setLineScaleSpringParams(lineScaleSpringParams);
lyricPlayer.setHidePassedLines(hidePassedLines);
}
$: {
lyricPlayer.setLyricLines(lyricLines);
lyricPlayer.update();
}
$: {
lyricPlayer.setCurrentTime(currentTime);
}
$: {
lineClickHandler = (e: Event) => onLyricLineClick(e as LyricLineMouseEvent);
lyricPlayer.addEventListener('line-click', lineClickHandler);
}
$: {
contextMenuHandler = (e: Event) => onLyricLineContextMenu(e as LyricLineMouseEvent);
lyricPlayer.addEventListener('line-contextmenu', contextMenuHandler);
}
onDestroy(() => {
animationCanceled = true;
lyricPlayer.dispose();
lyricPlayer.removeEventListener('line-contextmenu', contextMenuHandler);
lyricPlayer.removeEventListener('line-click', lineClickHandler);
});
</script>
<div bind:this={lyricsElement} class={className}></div>
<div bind:this={bottomLineElement}></div>