feat(lyrics): non-active lyric settings (#1954)

* feat: non-active lyric settings
This commit is contained in:
vimae
2026-04-21 08:09:03 +01:00
committed by GitHub
parent e5a8324a79
commit add0345f10
5 changed files with 77 additions and 5 deletions
+2
View File
@@ -540,6 +540,8 @@
"lyricOffset": "lyrics offset (ms)",
"lyricGap": "lyric gap",
"lyricSize": "lyric size",
"lyricOpacityNonActive": "non-active lyric opacity",
"lyricScaleNonActive": "non-active lyric scale",
"opacity": "opacity",
"showLyricMatch": "show lyric match",
"showLyricProvider": "show lyric provider",
@@ -17,6 +17,7 @@ import { MultiSelect } from '/@/shared/components/multi-select/multi-select';
import { NumberInput } from '/@/shared/components/number-input/number-input';
import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control';
import { Select } from '/@/shared/components/select/select';
import { Slider } from '/@/shared/components/slider/slider';
import { Stack } from '/@/shared/components/stack/stack';
import { Switch } from '/@/shared/components/switch/switch';
import { TextInput } from '/@/shared/components/text-input/text-input';
@@ -185,6 +186,48 @@ export const LyricsSettingsForm = ({ settingsKey }: LyricsSettingsFormProps) =>
postProcess: 'sentenceCase',
}),
},
{
control: (
<Slider
defaultValue={displaySettings.opacityNonActive}
label={(e) => (e * 100).toFixed(0) + '%'}
max={1.0}
min={0.0}
onChangeEnd={(e) => {
updateDisplaySetting({
opacityNonActive: e,
});
}}
step={0.01}
w={100}
/>
),
description: '',
title: t(`${t('page.fullscreenPlayer.config.lyricOpacityNonActive')}`, {
postProcess: 'sentenceCase',
}),
},
{
control: (
<Slider
defaultValue={displaySettings.scaleNonActive}
label={(e) => (e * 100).toFixed(0) + '%'}
max={1.0}
min={0.5}
onChangeEnd={(e) => {
updateDisplaySetting({
scaleNonActive: e,
});
}}
step={0.01}
w={100}
/>
),
description: '',
title: t(`${t('page.fullscreenPlayer.config.lyricScaleNonActive')}`, {
postProcess: 'sentenceCase',
}),
},
{
control: (
<Switch
@@ -4,14 +4,18 @@
line-height: 1.2;
color: var(--theme-colors-foreground);
word-break: normal;
opacity: 0.35;
opacity: var(--lyric-opacity);
transform: scale(var(--lyric-scale));
transform-origin: var(--lyric-scale-origin) center;
transition:
opacity 0.3s ease-in-out,
transform 0.3s ease-in-out;
opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform;
}
.lyric-line:global(.active) {
opacity: 1 !important;
transform: scale(1);
}
.lyric-line:global(.unsynchronized) {
@@ -49,6 +49,11 @@ export const SynchronizedLyrics = ({
? displaySettings.fontSize
: 24,
gap: displaySettings.gap && displaySettings.gap !== 0 ? displaySettings.gap : 24,
opacityNonActive: displaySettings.opacityNonActive,
scaleNonActive:
displaySettings.scaleNonActive && displaySettings.scaleNonActive !== 0
? displaySettings.scaleNonActive
: 0.95,
};
const { mediaSeekToTimestamp } = usePlayerActions();
const status = usePlayerStatus();
@@ -308,7 +313,18 @@ export const SynchronizedLyrics = ({
onMouseEnter={showScrollbar}
onMouseLeave={hideScrollbar}
ref={containerRef}
style={{ gap: `${settings.gap}px`, ...style }}
style={
{
// opacity/scale is set here for every lyric,
// and then overwritten by CSS for active lyrics
// to prevent expensive rerenders each lyric
'--lyric-opacity': settings.opacityNonActive,
'--lyric-scale': settings.scaleNonActive,
'--lyric-scale-origin': settings.alignment,
gap: `${settings.gap}px`,
...style,
} as React.CSSProperties
}
>
{settings.showProvider && source && (
<LyricLine
+8 -1
View File
@@ -536,6 +536,8 @@ const LyricsDisplaySettingsSchema = z.object({
fontSizeUnsync: z.number(),
gap: z.number(),
gapUnsync: z.number(),
opacityNonActive: z.number(),
scaleNonActive: z.number(),
});
const LyricsSettingsSchema = z.object({
@@ -1794,6 +1796,8 @@ const initialState: SettingsState = {
fontSizeUnsync: 24,
gap: 24,
gapUnsync: 24,
opacityNonActive: 0.2,
scaleNonActive: 0.95,
},
},
playback: {
@@ -2211,7 +2215,10 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
state.lyrics = mainSettings;
state.lyricsDisplay = {
default: displaySettings,
default: {
...state.lyricsDisplay.default,
...displaySettings,
},
};
}
}