mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
feat(lyrics): non-active lyric settings (#1954)
* feat: non-active lyric settings
This commit is contained in:
@@ -540,6 +540,8 @@
|
|||||||
"lyricOffset": "lyrics offset (ms)",
|
"lyricOffset": "lyrics offset (ms)",
|
||||||
"lyricGap": "lyric gap",
|
"lyricGap": "lyric gap",
|
||||||
"lyricSize": "lyric size",
|
"lyricSize": "lyric size",
|
||||||
|
"lyricOpacityNonActive": "non-active lyric opacity",
|
||||||
|
"lyricScaleNonActive": "non-active lyric scale",
|
||||||
"opacity": "opacity",
|
"opacity": "opacity",
|
||||||
"showLyricMatch": "show lyric match",
|
"showLyricMatch": "show lyric match",
|
||||||
"showLyricProvider": "show lyric provider",
|
"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 { NumberInput } from '/@/shared/components/number-input/number-input';
|
||||||
import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control';
|
import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control';
|
||||||
import { Select } from '/@/shared/components/select/select';
|
import { Select } from '/@/shared/components/select/select';
|
||||||
|
import { Slider } from '/@/shared/components/slider/slider';
|
||||||
import { Stack } from '/@/shared/components/stack/stack';
|
import { Stack } from '/@/shared/components/stack/stack';
|
||||||
import { Switch } from '/@/shared/components/switch/switch';
|
import { Switch } from '/@/shared/components/switch/switch';
|
||||||
import { TextInput } from '/@/shared/components/text-input/text-input';
|
import { TextInput } from '/@/shared/components/text-input/text-input';
|
||||||
@@ -185,6 +186,48 @@ export const LyricsSettingsForm = ({ settingsKey }: LyricsSettingsFormProps) =>
|
|||||||
postProcess: 'sentenceCase',
|
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: (
|
control: (
|
||||||
<Switch
|
<Switch
|
||||||
|
|||||||
@@ -4,14 +4,18 @@
|
|||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
color: var(--theme-colors-foreground);
|
color: var(--theme-colors-foreground);
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
opacity: 0.35;
|
opacity: var(--lyric-opacity);
|
||||||
|
transform: scale(var(--lyric-scale));
|
||||||
|
transform-origin: var(--lyric-scale-origin) center;
|
||||||
transition:
|
transition:
|
||||||
opacity 0.3s ease-in-out,
|
opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
transform 0.3s ease-in-out;
|
transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lyric-line:global(.active) {
|
.lyric-line:global(.active) {
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.lyric-line:global(.unsynchronized) {
|
.lyric-line:global(.unsynchronized) {
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ export const SynchronizedLyrics = ({
|
|||||||
? displaySettings.fontSize
|
? displaySettings.fontSize
|
||||||
: 24,
|
: 24,
|
||||||
gap: displaySettings.gap && displaySettings.gap !== 0 ? displaySettings.gap : 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 { mediaSeekToTimestamp } = usePlayerActions();
|
||||||
const status = usePlayerStatus();
|
const status = usePlayerStatus();
|
||||||
@@ -308,7 +313,18 @@ export const SynchronizedLyrics = ({
|
|||||||
onMouseEnter={showScrollbar}
|
onMouseEnter={showScrollbar}
|
||||||
onMouseLeave={hideScrollbar}
|
onMouseLeave={hideScrollbar}
|
||||||
ref={containerRef}
|
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 && (
|
{settings.showProvider && source && (
|
||||||
<LyricLine
|
<LyricLine
|
||||||
|
|||||||
@@ -536,6 +536,8 @@ const LyricsDisplaySettingsSchema = z.object({
|
|||||||
fontSizeUnsync: z.number(),
|
fontSizeUnsync: z.number(),
|
||||||
gap: z.number(),
|
gap: z.number(),
|
||||||
gapUnsync: z.number(),
|
gapUnsync: z.number(),
|
||||||
|
opacityNonActive: z.number(),
|
||||||
|
scaleNonActive: z.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const LyricsSettingsSchema = z.object({
|
const LyricsSettingsSchema = z.object({
|
||||||
@@ -1794,6 +1796,8 @@ const initialState: SettingsState = {
|
|||||||
fontSizeUnsync: 24,
|
fontSizeUnsync: 24,
|
||||||
gap: 24,
|
gap: 24,
|
||||||
gapUnsync: 24,
|
gapUnsync: 24,
|
||||||
|
opacityNonActive: 0.2,
|
||||||
|
scaleNonActive: 0.95,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
playback: {
|
playback: {
|
||||||
@@ -2211,7 +2215,10 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
|
|||||||
|
|
||||||
state.lyrics = mainSettings;
|
state.lyrics = mainSettings;
|
||||||
state.lyricsDisplay = {
|
state.lyricsDisplay = {
|
||||||
default: displaySettings,
|
default: {
|
||||||
|
...state.lyricsDisplay.default,
|
||||||
|
...displaySettings,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user