[feat] Add a dynamic image option to the fullscreen player (#526)

* Add an option for a dynamic background image in the fullscreen player

* Center the background image and fix some more bugs

* More cleaning up the background image

* Add option for customizable blur amount

* Fix missing translation key for image blur

* Fix dynamic image shifting when player is opened

* Hide image blur size config if dynamic background is disabled

---------

Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
This commit is contained in:
Benjamin 2024-03-05 02:30:37 -06:00 committed by GitHub
parent a45e7f24e4
commit d52d9136b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 76 additions and 8 deletions

View file

@ -311,6 +311,8 @@
"fullscreenPlayer": { "fullscreenPlayer": {
"config": { "config": {
"dynamicBackground": "dynamic background", "dynamicBackground": "dynamic background",
"dynamicImageBlur": "image blur size",
"dynamicIsImage": "enable background image",
"followCurrentLyric": "follow current lyric", "followCurrentLyric": "follow current lyric",
"lyricAlignment": "lyric alignment", "lyricAlignment": "lyric alignment",
"lyricGap": "lyric gap", "lyricGap": "lyric gap",

View file

@ -60,7 +60,11 @@ const ResponsiveContainer = styled.div`
} }
`; `;
const BackgroundImageOverlay = styled.div` interface BackgroundImageOverlayProps {
$blur: number;
}
const BackgroundImageOverlay = styled.div<BackgroundImageOverlayProps>`
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -68,12 +72,21 @@ const BackgroundImageOverlay = styled.div`
width: 100%; width: 100%;
height: 100%; height: 100%;
background: var(--bg-header-overlay); background: var(--bg-header-overlay);
backdrop-filter: blur(${({ $blur }) => $blur}rem);
`; `;
const mainBackground = 'var(--main-bg)';
const Controls = () => { const Controls = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { dynamicBackground, expanded, opacity, useImageAspectRatio } = const {
useFullScreenPlayerStore(); dynamicBackground,
dynamicImageBlur,
dynamicIsImage,
expanded,
opacity,
useImageAspectRatio,
} = useFullScreenPlayerStore();
const { setStore } = useFullScreenPlayerStoreActions(); const { setStore } = useFullScreenPlayerStoreActions();
const { setSettings } = useSettingsStoreActions(); const { setSettings } = useSettingsStoreActions();
const lyricConfig = useLyricsSettings(); const lyricConfig = useLyricsSettings();
@ -141,6 +154,45 @@ const Controls = () => {
/> />
</Option.Control> </Option.Control>
</Option> </Option>
{dynamicBackground && (
<Option>
<Option.Label>
{t('page.fullscreenPlayer.config.dynamicIsImage', {
postProcess: 'sentenceCase',
})}
</Option.Label>
<Option.Control>
<Switch
defaultChecked={dynamicIsImage}
onChange={(e) =>
setStore({
dynamicIsImage: e.target.checked,
})
}
/>
</Option.Control>
</Option>
)}
{dynamicBackground && dynamicIsImage && (
<Option>
<Option.Label>
{t('page.fullscreenPlayer.config.dynamicImageBlur', {
postProcess: 'sentenceCase',
})}
</Option.Label>
<Option.Control>
<Slider
defaultValue={dynamicImageBlur}
label={(e) => `${e} rem`}
max={6}
min={0}
step={0.5}
w="100%"
onChangeEnd={(e) => setStore({ dynamicImageBlur: Number(e) })}
/>
</Option.Control>
</Option>
)}
{dynamicBackground && ( {dynamicBackground && (
<Option> <Option>
<Option.Label> <Option.Label>
@ -368,9 +420,13 @@ const containerVariants: Variants = {
}; };
}, },
open: (custom) => { open: (custom) => {
const { dynamicBackground, background, windowBarStyle } = custom; const { background, backgroundImage, dynamicBackground, windowBarStyle } = custom;
return { return {
background: dynamicBackground ? background : 'var(--main-bg)', background: dynamicBackground ? backgroundImage : mainBackground,
backgroundColor: dynamicBackground ? background : mainBackground,
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
height: height:
windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS
? 'calc(100vh - 120px)' ? 'calc(100vh - 120px)'
@ -394,7 +450,7 @@ const containerVariants: Variants = {
}; };
export const FullScreenPlayer = () => { export const FullScreenPlayer = () => {
const { dynamicBackground } = useFullScreenPlayerStore(); const { dynamicBackground, dynamicImageBlur, dynamicIsImage } = useFullScreenPlayerStore();
const { setStore } = useFullScreenPlayerStoreActions(); const { setStore } = useFullScreenPlayerStoreActions();
const { windowBarStyle } = useWindowSettings(); const { windowBarStyle } = useWindowSettings();
@ -416,17 +472,23 @@ export const FullScreenPlayer = () => {
srcLoaded: true, srcLoaded: true,
}); });
const imageUrl = currentSong?.imageUrl;
const backgroundImage =
imageUrl && dynamicIsImage
? `url("${imageUrl.replace(/size=\d+/g, 'size=500')}`
: mainBackground;
return ( return (
<Container <Container
animate="open" animate="open"
custom={{ background, dynamicBackground, windowBarStyle }} custom={{ background, backgroundImage, dynamicBackground, windowBarStyle }}
exit="closed" exit="closed"
initial="closed" initial="closed"
transition={{ duration: 2 }} transition={{ duration: 2 }}
variants={containerVariants} variants={containerVariants}
> >
<Controls /> <Controls />
{dynamicBackground && <BackgroundImageOverlay />} {dynamicBackground && <BackgroundImageOverlay $blur={dynamicImageBlur} />}
<ResponsiveContainer> <ResponsiveContainer>
<FullScreenPlayerImage /> <FullScreenPlayerImage />
<FullScreenPlayerQueue /> <FullScreenPlayerQueue />

View file

@ -6,6 +6,8 @@ import { immer } from 'zustand/middleware/immer';
interface FullScreenPlayerState { interface FullScreenPlayerState {
activeTab: string | 'queue' | 'related' | 'lyrics'; activeTab: string | 'queue' | 'related' | 'lyrics';
dynamicBackground?: boolean; dynamicBackground?: boolean;
dynamicImageBlur: number;
dynamicIsImage?: boolean;
expanded: boolean; expanded: boolean;
opacity: number; opacity: number;
useImageAspectRatio: boolean; useImageAspectRatio: boolean;
@ -28,6 +30,8 @@ export const useFullScreenPlayerStore = create<FullScreenPlayerSlice>()(
}, },
activeTab: 'queue', activeTab: 'queue',
dynamicBackground: true, dynamicBackground: true,
dynamicImageBlur: 1.5,
dynamicIsImage: false,
expanded: false, expanded: false,
opacity: 60, opacity: 60,
useImageAspectRatio: false, useImageAspectRatio: false,