feat(synced-lyrics): Better-Lyrics Styling for Synced-Lyrics (#2554)

Co-authored-by: JellyBrick <shlee1503@naver.com>
This commit is contained in:
KimJammer
2024-12-24 18:15:36 -05:00
committed by GitHub
parent 2bf67b941e
commit 51da259c97
7 changed files with 216 additions and 62 deletions

View File

@ -725,6 +725,10 @@
"line-effect": { "line-effect": {
"label": "Line effect", "label": "Line effect",
"submenu": { "submenu": {
"fancy": {
"label": "Fancy",
"tooltip": "Use large, app-like effects on the current line"
},
"focus": { "focus": {
"label": "Focus", "label": "Focus",
"tooltip": "Make only the current line white" "tooltip": "Make only the current line white"

View File

@ -10,7 +10,7 @@ import type { SyncedLyricsPluginConfig } from './types';
export default createPlugin({ export default createPlugin({
name: () => t('plugins.synced-lyrics.name'), name: () => t('plugins.synced-lyrics.name'),
description: () => t('plugins.synced-lyrics.description'), description: () => t('plugins.synced-lyrics.description'),
authors: ['Non0reo', 'ArjixWasTaken'], authors: ['Non0reo', 'ArjixWasTaken', 'KimJammer'],
restartNeeded: true, restartNeeded: true,
addedVersion: '3.5.X', addedVersion: '3.5.X',
config: { config: {
@ -19,8 +19,8 @@ export default createPlugin({
showLyricsEvenIfInexact: true, showLyricsEvenIfInexact: true,
showTimeCodes: false, showTimeCodes: false,
defaultTextString: '♪', defaultTextString: '♪',
lineEffect: 'scale', lineEffect: 'fancy',
} as SyncedLyricsPluginConfig, } satisfies SyncedLyricsPluginConfig,
menu, menu,
renderer, renderer,

View File

@ -5,9 +5,9 @@ import { t } from '@/i18n';
import type { MenuContext } from '@/types/contexts'; import type { MenuContext } from '@/types/contexts';
import type { SyncedLyricsPluginConfig } from './types'; import type { SyncedLyricsPluginConfig } from './types';
export const menu = async (ctx: MenuContext<SyncedLyricsPluginConfig>): Promise< export const menu = async (
MenuItemConstructorOptions[] ctx: MenuContext<SyncedLyricsPluginConfig>,
> => { ): Promise<MenuItemConstructorOptions[]> => {
const config = await ctx.getConfig(); const config = await ctx.getConfig();
return [ return [
@ -27,6 +27,21 @@ export const menu = async (ctx: MenuContext<SyncedLyricsPluginConfig>): Promise<
toolTip: t('plugins.synced-lyrics.menu.line-effect.tooltip'), toolTip: t('plugins.synced-lyrics.menu.line-effect.tooltip'),
type: 'submenu', type: 'submenu',
submenu: [ submenu: [
{
label: t(
'plugins.synced-lyrics.menu.line-effect.submenu.fancy.label',
),
toolTip: t(
'plugins.synced-lyrics.menu.line-effect.submenu.fancy.tooltip',
),
type: 'radio',
checked: config.lineEffect === 'fancy',
click() {
ctx.setConfig({
lineEffect: 'fancy',
});
},
},
{ {
label: t( label: t(
'plugins.synced-lyrics.menu.line-effect.submenu.scale.label', 'plugins.synced-lyrics.menu.line-effect.submenu.scale.label',

View File

@ -1,4 +1,4 @@
import { createEffect, createMemo } from 'solid-js'; import { createEffect, createMemo, For } from 'solid-js';
import { currentTime } from './LyricsContainer'; import { currentTime } from './LyricsContainer';
@ -32,7 +32,16 @@ export const SyncedLine = ({ line }: SyncedLineProps) => {
return config()?.defaultTextString ?? ''; return config()?.defaultTextString ?? '';
}); });
// prettier-ignore if (!text()) {
return (
<yt-formatted-string
text={{
runs: [{ text: '' }],
}}
/>
);
}
return ( return (
<div <div
ref={ref} ref={ref}
@ -41,14 +50,33 @@ export const SyncedLine = ({ line }: SyncedLineProps) => {
_ytAPI?.seekTo(line.timeInMs / 1000); _ytAPI?.seekTo(line.timeInMs / 1000);
}} }}
> >
<yt-formatted-string <div class="text-lyrics description ytmusic-description-shelf-renderer">
class="text-lyrics description ytmusic-description-shelf-renderer" <yt-formatted-string
text={{ text={{
runs: [ runs: [{ text: config()?.showTimeCodes ? `[${line.time}] ` : '' }],
{ text: config()?.showTimeCodes ? `[${line.time}]` : '' }, }}
{ text: text() }], />
}}
/> <For each={text().split(' ')}>
{(word, index) => {
return (
<span
style={{
'transition-delay': `${index() * 0.05}s`,
'animation-delay': `${index() * 0.05}s`,
'--lyrics-duration:': `${line.duration / 1000}s;`,
}}
>
<yt-formatted-string
text={{
runs: [{ text: `${word} ` }],
}}
/>
</span>
);
}}
</For>
</div>
</div> </div>
); );
}; };

View File

@ -17,35 +17,85 @@ createEffect(() => {
// Set the line effect // Set the line effect
switch (config()?.lineEffect) { switch (config()?.lineEffect) {
case 'scale': case 'fancy':
root.style.setProperty('--lyrics-font-size', '3rem');
root.style.setProperty('--lyrics-line-height', '1.333');
root.style.setProperty('--lyrics-width', '100%');
root.style.setProperty('--lyrics-padding', '2rem');
root.style.setProperty( root.style.setProperty(
'--previous-lyrics', '--lyrics-animations',
'var(--ytmusic-text-primary)', 'lyrics-glow var(--lyrics-glow-duration) forwards, lyrics-wobble var(--lyrics-wobble-duration) forwards',
); );
root.style.setProperty('--current-lyrics', 'var(--ytmusic-text-primary)');
root.style.setProperty('--size-lyrics', '1.2'); root.style.setProperty('--lyrics-inactive-font-weight', '700');
root.style.setProperty('--offset-lyrics', '0'); root.style.setProperty('--lyrics-inactive-opacity', '0.33');
root.style.setProperty('--lyric-width', '83%'); root.style.setProperty('--lyrics-inactive-scale', '0.95');
root.style.setProperty('--lyrics-inactive-offset', '0');
root.style.setProperty('--lyrics-active-font-weight', '700');
root.style.setProperty('--lyrics-active-opacity', '1');
root.style.setProperty('--lyrics-active-scale', '1');
root.style.setProperty('--lyrics-active-offset', '0');
break;
case 'scale':
root.style.setProperty('--lyrics-font-size', '1.4rem');
root.style.setProperty(
'--lyrics-line-height',
'var(--ytmusic-body-line-height)',
);
root.style.setProperty('--lyrics-width', '83%');
root.style.setProperty('--lyrics-padding', '0');
root.style.setProperty('--lyrics-animations', 'none');
root.style.setProperty('--lyrics-inactive-font-weight', '400');
root.style.setProperty('--lyrics-inactive-opacity', '0.33');
root.style.setProperty('--lyrics-inactive-scale', '1');
root.style.setProperty('--lyrics-inactive-offset', '0');
root.style.setProperty('--lyrics-active-font-weight', '700');
root.style.setProperty('--lyrics-active-opacity', '1');
root.style.setProperty('--lyrics-active-scale', '1.2');
root.style.setProperty('--lyrics-active-offset', '0');
break; break;
case 'offset': case 'offset':
root.style.setProperty('--lyrics-font-size', '1.4rem');
root.style.setProperty( root.style.setProperty(
'--previous-lyrics', '--lyrics-line-height',
'var(--ytmusic-text-primary)', 'var(--ytmusic-body-line-height)',
); );
root.style.setProperty('--current-lyrics', 'var(--ytmusic-text-primary)'); root.style.setProperty('--lyrics-width', '100%');
root.style.setProperty('--size-lyrics', '1'); root.style.setProperty('--lyrics-padding', '0');
root.style.setProperty('--offset-lyrics', '5%'); root.style.setProperty('--lyrics-animations', 'none');
root.style.setProperty('--lyric-width', '100%');
root.style.setProperty('--lyrics-inactive-font-weight', '400');
root.style.setProperty('--lyrics-inactive-opacity', '0.33');
root.style.setProperty('--lyrics-inactive-scale', '1');
root.style.setProperty('--lyrics-inactive-offset', '0');
root.style.setProperty('--lyrics-active-font-weight', '700');
root.style.setProperty('--lyrics-active-opacity', '1');
root.style.setProperty('--lyrics-active-scale', '1');
root.style.setProperty('--lyrics-active-offset', '5%');
break; break;
case 'focus': case 'focus':
root.style.setProperty('--lyrics-font-size', '1.4rem');
root.style.setProperty( root.style.setProperty(
'--previous-lyrics', '--lyrics-line-height',
'var(--ytmusic-text-secondary)', 'var(--ytmusic-body-line-height)',
); );
root.style.setProperty('--current-lyrics', 'var(--ytmusic-text-primary)'); root.style.setProperty('--lyrics-width', '100%');
root.style.setProperty('--size-lyrics', '1'); root.style.setProperty('--lyrics-padding', '0');
root.style.setProperty('--offset-lyrics', '0'); root.style.setProperty('--lyrics-animations', 'none');
root.style.setProperty('--lyric-width', '100%');
root.style.setProperty('--lyrics-inactive-font-weight', '400');
root.style.setProperty('--lyrics-inactive-opacity', '0.33');
root.style.setProperty('--lyrics-inactive-scale', '1');
root.style.setProperty('--lyrics-inactive-offset', '0');
root.style.setProperty('--lyrics-active-font-weight', '700');
root.style.setProperty('--lyrics-active-opacity', '1');
root.style.setProperty('--lyrics-active-scale', '1');
root.style.setProperty('--lyrics-active-offset', '0');
break; break;
} }
}); });

View File

@ -8,18 +8,40 @@
display: block !important; display: block !important;
} }
/* :root { /* Variables are overridden by selected line effect */
--ytmusic-text-primary: #fff;
--ytmusic-text-secondary: #aaa;
} */
:root { :root {
/* Layout */
--global-margin: 0.7rem; --global-margin: 0.7rem;
--previous-lyrics: var(--ytmusic-text-primary); --lyrics-padding: 0;
--current-lyrics: var(--ytmusic-text-primary);
--upcoming-lyrics: var(--ytmusic-text-secondary); /* Typography */
--size-lyrics: 1.2em; --lyrics-font-family: Satoshi, Avenir, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell,
--offset-lyrics: 1em; Open Sans, Helvetica Neue, sans-serif;
--lyrics-font-size: 1.4rem;
--lyrics-line-height: var(--ytmusic-body-line-height);
--lyrics-width: 100%;
/* Inactive Lyrics */
--lyrics-inactive-font-weight: 400;
--lyrics-inactive-opacity: 0.33;
--lyrics-inactive-scale: 1;
--lyrics-inactive-offset: 0;
/* Active Lyrics */
--lyrics-active-font-weight: 700;
--lyrics-active-opacity: 1;
--lyrics-active-scale: 1;
--lyrics-active-offset: 0;
/* Animations */
--lyrics-animations: lyrics-glow var(--lyrics-glow-duration) forwards, lyrics-wobble var(--lyrics-wobble-duration) forwards;
--lyrics-scale-duration: 0.166s;
--lyrics-opacity-transition: 0.33s;
--lyrics-glow-duration: var(--lyrics-duration, 2s);
--lyrics-wobble-duration: calc(var(--lyrics-duration, 2s) / 2);
/* Colors */
--glow-color: rgba(255, 255, 255, 0.5);
} }
.lyric-container { .lyric-container {
@ -32,7 +54,7 @@
} }
.synced-line { .synced-line {
width: var(--lyric-width, 100%); width: var(--lyrics-width, 100%);
& > .text-lyrics { & > .text-lyrics {
cursor: pointer; cursor: pointer;
@ -43,8 +65,7 @@
display: block; display: block;
justify-content: left; justify-content: left;
text-align: left; text-align: left;
margin: 0.5rem 0; margin: 0.5rem 20px 0.5rem 0;
margin-right: 20px;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
@ -54,35 +75,46 @@
} }
.text-lyrics { .text-lyrics {
font-family: var(--lyrics-font-family) !important;
font-size: var(--lyrics-font-size) !important;
font-weight: var(--lyrics-inactive-font-weight) !important;
line-height: var(--lyrics-line-height) !important;
padding-top: var(--lyrics-padding);
padding-bottom: var(--lyrics-padding);
scale: var(--lyrics-inactive-scale);
translate: var(--lyrics-inactive-offset);
transition:
scale var(--lyrics-scale-duration),
translate 0.3s ease-in-out;
display: block; display: block;
text-align: left; text-align: left;
margin: var(--global-margin) 0; margin: var(--global-margin) 0;
transition:
scale 0.3s ease-in-out,
translate 0.3s ease-in-out,
color 0.1s ease-in-out;
transform-origin: 0 50%; transform-origin: 0 50%;
} }
.text-lyrics > span { .text-lyrics > span {
margin-inline: 0.1em; display: inline-block;
white-space: pre-wrap;
opacity: var(--lyrics-inactive-opacity);
transition: opacity var(--lyrics-opacity-transition);
} }
.previous > .text-lyrics { .previous > .text-lyrics {
color: var(--previous-lyrics);
font-weight: normal;
} }
.current > .text-lyrics { .current > .text-lyrics {
color: var(--current-lyrics); font-weight: var(--lyrics-active-font-weight) !important;
font-weight: bold; scale: var(--lyrics-active-scale);
scale: var(--size-lyrics); translate: var(--lyrics-active-offset);
translate: var(--offset-lyrics) 0; }
.current > .text-lyrics > span {
opacity: var(--lyrics-active-opacity);
animation: var(--lyrics-animations);
} }
.upcoming > .text-lyrics { .upcoming > .text-lyrics {
color: var(--upcoming-lyrics);
font-weight: normal;
} }
.lyrics-renderer { .lyrics-renderer {
@ -162,3 +194,28 @@
transition: top 325ms ease-in-out; transition: top 325ms ease-in-out;
} }
/* Animations */
@keyframes lyrics-wobble {
from {
transform: translateY(0px);
}
33.33% {
transform: translateY(1.75px);
}
66.66% {
transform: translateY(-1.75px);
}
to {
transform: translateY(0px);
}
}
@keyframes lyrics-glow {
0% {
text-shadow: 0 0 1.5rem var(--glow-color);
}
to {
text-shadow: 0 0 0 var(--glow-color);
}
}

View File

@ -20,7 +20,7 @@ export type LineLyrics = {
status: LineLyricsStatus; status: LineLyricsStatus;
}; };
export type LineEffect = 'scale' | 'offset' | 'focus'; export type LineEffect = 'fancy' | 'scale' | 'offset' | 'focus';
export interface LyricResult { export interface LyricResult {
title: string; title: string;