improve: the effect when two lyrics overlap

This commit is contained in:
alikia2x (寒寒) 2024-10-29 02:45:36 +08:00
parent f939472329
commit 5988c8335c
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
4 changed files with 47 additions and 39 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "aquavox", "name": "aquavox",
"version": "2.3.3", "version": "2.5.1",
"private": false, "private": false,
"module": "index.ts", "module": "index.ts",
"type": "module", "type": "module",

View File

@ -4,11 +4,16 @@
import type { LyricPos } from './type'; import type { LyricPos } from './type';
import type { Spring } from '@core/graphics/spring/spring'; import type { Spring } from '@core/graphics/spring/spring';
const viewportWidth = document.documentElement.clientWidth;
const blurRatio = viewportWidth > 640 ? 1 : 1.4;
export let line: ScriptItem; export let line: ScriptItem;
export let index: number; export let index: number;
export let debugMode: Boolean; export let debugMode: Boolean;
export let lyricClick: Function; export let lyricClick: Function;
export let progress: number; export let progress: number;
export let currentLyricIndex: number;
export let scrolling: boolean;
let ref: HTMLDivElement; let ref: HTMLDivElement;
let clickMask: HTMLSpanElement; let clickMask: HTMLSpanElement;
@ -75,9 +80,21 @@
opacity = isCurrent ? 1 : 0.36; opacity = isCurrent ? 1 : 0.36;
}; };
export const setBlur = (blur: number) => { $: {
ref.style.filter = `blur(${blur}px)`; if (ref && ref.style) {
}; let blurRadius = 0;
const offset = Math.abs(index - currentLyricIndex);
if (progress > line.end) {
blurRadius = Math.min(offset * blurRatio, 16);
} else if (line.start <= progress && progress <= line.end) {
blurRadius = 0;
} else {
blurRadius = Math.min(offset * blurRatio, 16);
}
if (scrolling) blurRadius=0;
ref.style.filter = `blur(${blurRadius}px)`;
}
}
export const update = (pos: LyricPos, delay: number = 0) => { export const update = (pos: LyricPos, delay: number = 0) => {
if (lastPosX === undefined || lastPosY === undefined) { if (lastPosX === undefined || lastPosY === undefined) {
@ -140,7 +157,7 @@
<span <span
bind:this={clickMask} bind:this={clickMask}
class="absolute w-[calc(100%-2.5rem)] lg:w-[calc(100%-3rem)] h-full 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)]" -translate-x-2 lg:-translate-x-5 -translate-y-5 rounded-lg duration-300 lg:hover:bg-[rgba(255,255,255,.15)] z-[100] "
> >
</span> </span>
{#if debugMode} {#if debugMode}
@ -148,25 +165,25 @@
{index}: duration: {(line.end - line.start).toFixed(3)}, {line.start.toFixed(3)}~{line.end.toFixed(3)} {index}: duration: {(line.end - line.start).toFixed(3)}, {line.start.toFixed(3)}~{line.end.toFixed(3)}
</span> </span>
{/if} {/if}
{#if line.words} {#if line.words !== undefined && line.words.length > 0}
<span <span
class={`text-white text-[2rem] leading-9 lg:text-5xl lg:leading-[4rem] font-semibold mr-4 `} class={`text-white text-[2rem] leading-9 lg:text-5xl lg:leading-[4rem] font-semibold mr-4 `}
> >
{#each line.words as word} {#each line.words as word}
{#if word.word} {#if word.word}
{#each word.word.split("") as chr, i} {#each word.word.split("") as chr, i}
<span <span
class={(line.start <= progress && progress <= line.end && progress > (word.endTime - word.startTime) * ((i)/word.word.length) + word.startTime ? "opacity-100" : "opacity-35") + " inline-block duration-300"} class={(line.start <= progress && progress <= line.end && progress > (word.endTime - word.startTime) * ((i)/word.word.length) + word.startTime ? "opacity-100 text-glow" : "opacity-35") + " inline-block duration-300"}
> >
{chr} {chr}
</span> </span>
{/each} {/each}
{/if} {/if}
{/each} {/each}
</span> </span>
{:else} {:else}
<span <span
class={`text-white text-[2rem] leading-9 lg:text-5xl lg:leading-[4rem] font-semibold mr-4 duration-200 ${line.start <= progress && progress <= line.end ? "opacity-100" : "opacity-35"}`} class={`text-white text-[2rem] leading-9 lg:text-5xl lg:leading-[4rem] font-semibold mr-4 duration-200 ${line.start <= progress && progress <= line.end ? "opacity-100 text-glow" : "opacity-35"}`}
> >
{line.text} {line.text}
</span> </span>
@ -179,3 +196,14 @@
</span> </span>
{/if} {/if}
</div> </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

@ -75,17 +75,15 @@
const relativeOrigin = lyricTopList[currentLyricIndex] - currentLyricTop; const relativeOrigin = lyricTopList[currentLyricIndex] - currentLyricTop;
for (let i = 0; i < lyricElements.length; i++) { for (let i = 0; i < lyricElements.length; i++) {
const currentLyricComponent = lyricComponents[i]; const currentLyricComponent = lyricComponents[i];
const lyric = originalLyrics.scripts[i];
let delay = 0; let delay = 0;
if (i < currentLyricIndex) { if (progress > lyric.end) {
delay = 0; delay = 0;
} else if (i == currentLyricIndex) { } else if (lyric.start <= progress && progress <= lyric.end) {
delay = 0.042; delay = 0.042;
} else { } else {
delay = Math.min(Math.min(currentLyricDuration, 0.6), 0.067 * (i - currentLyricIndex + 1.2)); delay = Math.min(Math.min(currentLyricDuration, 0.6), 0.067 * (i - currentLyricIndex + 1.2));
} }
const offset = Math.abs(i - currentLyricIndex);
let blurRadius = Math.min(offset * blurRatio, 16);
currentLyricComponent.setBlur(blurRadius);
currentLyricComponent.update({ x: 0, y: lyricTopList[i] - relativeOrigin }, delay); currentLyricComponent.update({ x: 0, y: lyricTopList[i] - relativeOrigin }, delay);
} }
} }
@ -107,7 +105,7 @@
for (let i = 0; i < lyricElements.length; i++) { for (let i = 0; i < lyricElements.length; i++) {
const currentLyricComponent = lyricComponents[i]; const currentLyricComponent = lyricComponents[i];
const currentY = currentLyricComponent.getInfo().y; const currentY = currentLyricComponent.getInfo().y;
currentLyricComponent.setBlur(0); scrolling = true;
currentLyricComponent.stop(); currentLyricComponent.stop();
currentLyricComponent.setY(currentY - deltaY); currentLyricComponent.setY(currentY - deltaY);
} }
@ -263,7 +261,7 @@
bind:this={lyricsContainer} bind:this={lyricsContainer}
> >
{#each lyricLines as lyric, i} {#each lyricLines as lyric, i}
<LyricLine line={lyric} index={i} bind:this={lyricComponents[i]} {debugMode} {lyricClick} {progress} /> <LyricLine line={lyric} index={i} bind:this={lyricComponents[i]} {debugMode} {lyricClick} {progress} {currentLyricIndex} {scrolling}/>
{/each} {/each}
</div> </div>
{/if} {/if}

View File

@ -16,14 +16,13 @@ import {
tok, tok,
type Token type Token
} from 'typescript-parsec'; } from 'typescript-parsec';
import type { LrcJsonData, ParsedLrc, ScriptItem, ScriptWordsItem } from '../type'; import type { LrcJsonData, ParsedLrc, ScriptItem } from '../type';
import type { IDTag } from './type'; import type { IDTag } from './type';
interface ParserScriptItem { interface ParserScriptItem {
start: number; start: number;
text: string; text: string;
words?: ScriptWordsItem[];
translation?: string; translation?: string;
singer?: string; singer?: string;
} }
@ -166,25 +165,10 @@ function lrcLine(
.filter((s) => s.trim().length > 0) .filter((s) => s.trim().length > 0)
.join(wordDiv); .join(wordDiv);
const words = mainPart
.filter((s) => joinTokens(s[1]).trim().length > 0)
.map((s) => {
const wordBegin = s[0];
const word = s[1];
let ret: Partial<ScriptWordsItem> = { start: wordBegin };
if (word[0]) {
ret.beginIndex = word[0].pos.columnBegin - 1;
}
if (word[word.length - 1]) {
ret.endIndex = word[word.length - 1].pos.columnEnd;
}
return ret as ScriptWordsItem; // TODO: Complete this
});
const singer = singerPart?.text; const singer = singerPart?.text;
const translation = translatePart === undefined ? undefined : joinTokens(translatePart); const translation = translatePart === undefined ? undefined : joinTokens(translatePart);
return ['script_item', { start, text, words, singer, translation } as ParserScriptItem]; return ['script_item', { start, text, singer, translation } as ParserScriptItem];
}), }),
apply(lrcTag, (r) => ['lrc_tag', r as IDTag]), apply(lrcTag, (r) => ['lrc_tag', r as IDTag]),
apply(seq(spaces(), str('#'), unicodeStr), (cmt) => ['comment', cmt[2].join('')] as const), apply(seq(spaces(), str('#'), unicodeStr), (cmt) => ['comment', cmt[2].join('')] as const),
@ -206,8 +190,6 @@ export function parseLRC(
const tokenizer = buildLexer([ const tokenizer = buildLexer([
[true, /^\[/gu, '['], [true, /^\[/gu, '['],
[true, /^\]/gu, ']'], [true, /^\]/gu, ']'],
[true, /^</gu, '<'],
[true, /^>/gu, '>'],
[true, /^\|/gu, '|'], [true, /^\|/gu, '|'],
[true, /^./gu, 'char'] [true, /^./gu, 'char']
]); ]);