feat: 优化歌词背景色 加载问题

This commit is contained in:
alger
2024-09-18 15:11:20 +08:00
parent 6dc14ec51b
commit eb2ea1981d
8 changed files with 99 additions and 22 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ function createWindow() {
win.setMinimumSize(1200, 780); win.setMinimumSize(1200, 780);
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
win.webContents.openDevTools({ mode: 'detach' }); win.webContents.openDevTools({ mode: 'detach' });
win.loadURL('http://localhost:4678/'); win.loadURL('http://localhost:7788/');
} else { } else {
win.loadURL(`file://${__dirname}/dist/index.html`); win.loadURL(`file://${__dirname}/dist/index.html`);
} }
+21 -1
View File
@@ -1,6 +1,16 @@
<template> <template>
<div class="song-item" :class="{ 'song-mini': mini }"> <div class="song-item" :class="{ 'song-mini': mini }">
<n-image v-if="item.picUrl" :src="getImgUrl(item.picUrl, '40y40')" class="song-item-img" lazy preview-disabled /> <n-image
v-if="item.picUrl"
ref="songImg"
:src="getImgUrl(item.picUrl, '40y40')"
class="song-item-img"
preview-disabled
:img-props="{
crossorigin: 'anonymous',
}"
@load="imageLoad"
/>
<div class="song-item-content"> <div class="song-item-content">
<div class="song-item-content-title"> <div class="song-item-content-title">
<n-ellipsis class="text-ellipsis" line-clamp="1">{{ item.name }}</n-ellipsis> <n-ellipsis class="text-ellipsis" line-clamp="1">{{ item.name }}</n-ellipsis>
@@ -30,10 +40,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useTemplateRef } from 'vue';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import type { SongResult } from '@/type/music'; import type { SongResult } from '@/type/music';
import { getImgUrl } from '@/utils'; import { getImgUrl } from '@/utils';
import { getImageBackground } from '@/utils/linearColor';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -60,6 +72,14 @@ const isPlaying = computed(() => {
const emits = defineEmits(['play']); const emits = defineEmits(['play']);
const songImageRef = useTemplateRef('songImg');
const imageLoad = async () => {
const background = await getImageBackground((songImageRef.value as any).imageRef as unknown as HTMLImageElement);
// eslint-disable-next-line vue/no-mutating-props
props.item.backgroundColor = background;
};
// 播放音乐 设置音乐详情 打开音乐底栏 // 播放音乐 设置音乐详情 打开音乐底栏
const playMusicEvent = async (item: SongResult) => { const playMusicEvent = async (item: SongResult) => {
if (playMusic.value.id === item.id) { if (playMusic.value.id === item.id) {
+16 -4
View File
@@ -1,6 +1,6 @@
<template> <template>
<div class="layout-page"> <div class="layout-page">
<div class="layout-main"> <div class="layout-main" :style="{ background: backgroundColor }">
<title-bar v-if="isElectron" /> <title-bar v-if="isElectron" />
<div class="layout-main-page" :class="isElectron ? '' : 'pt-6'"> <div class="layout-main-page" :class="isElectron ? '' : 'pt-6'">
<!-- 侧边菜单栏 --> <!-- 侧边菜单栏 -->
@@ -9,7 +9,7 @@
<!-- 搜索栏 --> <!-- 搜索栏 -->
<search-bar /> <search-bar />
<!-- 主页面路由 --> <!-- 主页面路由 -->
<div class="main-content bg-black" :native-scrollbar="false"> <div class="main-content" :native-scrollbar="false">
<n-message-provider> <n-message-provider>
<router-view <router-view
v-slot="{ Component }" v-slot="{ Component }"
@@ -70,6 +70,18 @@ const audio = {
value: document.querySelector('#MusicAudio') as HTMLAudioElement, value: document.querySelector('#MusicAudio') as HTMLAudioElement,
}; };
const backgroundColor = ref('#000');
// watch(
// () => store.state.playMusic,
// () => {
// backgroundColor.value = store.state.playMusic.backgroundColor;
// console.log('backgroundColor.value', backgroundColor.value);
// },
// {
// immediate: true,
// deep: true,
// },
// );
onMounted(() => { onMounted(() => {
// 监听音乐是否播放 // 监听音乐是否播放
watch( watch(
@@ -118,11 +130,11 @@ const playMusicEvent = async () => {
.layout-page { .layout-page {
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
@apply flex justify-center items-center overflow-hidden; @apply flex justify-center items-center overflow-hidden bg-black;
} }
.layout-main { .layout-main {
@apply bg-black text-white shadow-xl flex flex-col relative; @apply text-white shadow-xl flex flex-col relative transition-all;
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
+1 -1
View File
@@ -112,7 +112,7 @@ defineExpose({
} }
.drawer-back { .drawer-back {
@apply absolute bg-cover bg-center; @apply absolute bg-cover bg-center;
filter: brightness(80%); // filter: brightness(80%);
z-index: -1; z-index: -1;
width: 200%; width: 200%;
height: 200%; height: 200%;
+1 -1
View File
@@ -127,7 +127,7 @@ watch(
() => store.state.playMusic, () => store.state.playMusic,
async () => { async () => {
loadLrc(playMusic.value.id); loadLrc(playMusic.value.id);
background.value = await getImageLinearBackground(getImgUrl(playMusic.value?.picUrl, '300y300')); background.value = playMusic.value.backgroundColor as string;
}, },
{ immediate: true, deep: true }, { immediate: true, deep: true },
); );
+21 -11
View File
@@ -4,7 +4,8 @@ import { getMusicUrl, getParsingMusicUrl } from '@/api/music';
import { useMusicHistory } from '@/hooks/MusicHistoryHook'; import { useMusicHistory } from '@/hooks/MusicHistoryHook';
import homeRouter from '@/router/home'; import homeRouter from '@/router/home';
import type { SongResult } from '@/type/music'; import type { SongResult } from '@/type/music';
import { getMusicProxyUrl } from '@/utils'; import { getImgUrl, getMusicProxyUrl } from '@/utils';
import { getImageLinearBackground } from '@/utils/linearColor';
interface State { interface State {
menus: any[]; menus: any[];
@@ -47,12 +48,7 @@ const mutations = {
state.menus = menus; state.menus = menus;
}, },
async setPlay(state: State, playMusic: SongResult) { async setPlay(state: State, playMusic: SongResult) {
state.playMusic = { ...playMusic, playLoading: true }; await getSongDetail(state, playMusic);
state.playMusicUrl = await getSongUrl(playMusic.id);
state.play = true;
musicHistory.addMusic(playMusic);
state.playMusic.playLoading = false;
state.playListIndex = state.playList.findIndex((item) => item.id === playMusic.id);
}, },
setIsPlay(state: State, isPlay: boolean) { setIsPlay(state: State, isPlay: boolean) {
state.isPlay = isPlay; state.isPlay = isPlay;
@@ -69,16 +65,16 @@ const mutations = {
state.play = true; state.play = true;
return; return;
} }
state.playListIndex = (state.playListIndex + 1) % state.playList.length; const playListIndex = (state.playListIndex + 1) % state.playList.length;
await updatePlayMusic(state); await getSongDetail(state, state.playList[playListIndex]);
}, },
async prevPlay(state: State) { async prevPlay(state: State) {
if (state.playList.length === 0) { if (state.playList.length === 0) {
state.play = true; state.play = true;
return; return;
} }
state.playListIndex = (state.playListIndex - 1 + state.playList.length) % state.playList.length; const playListIndex = (state.playListIndex - 1 + state.playList.length) % state.playList.length;
await updatePlayMusic(state); await getSongDetail(state, state.playList[playListIndex]);
}, },
async setSetData(state: State, setData: any) { async setSetData(state: State, setData: any) {
state.setData = setData; state.setData = setData;
@@ -108,6 +104,20 @@ const updatePlayMusic = async (state: State) => {
musicHistory.addMusic(state.playMusic); musicHistory.addMusic(state.playMusic);
}; };
const getSongDetail = async (state: State, playMusic: SongResult) => {
state.playMusic.playLoading = true;
state.playMusicUrl = await getSongUrl(playMusic.id);
const backgroundColor = playMusic.backgroundColor
? playMusic.backgroundColor
: await getImageLinearBackground(getImgUrl(playMusic?.picUrl, '30y30'));
state.playMusic = { ...playMusic, backgroundColor };
// state.playMusic = { ...playMusic };
state.play = true;
musicHistory.addMusic(playMusic);
state.playListIndex = state.playList.findIndex((item) => item.id === playMusic.id);
state.playMusic.playLoading = false;
};
const store = createStore({ const store = createStore({
state, state,
mutations, mutations,
+1
View File
@@ -18,6 +18,7 @@ export interface SongResult {
playLoading?: boolean; playLoading?: boolean;
ar?: Artist[]; ar?: Artist[];
al?: Album; al?: Album;
backgroundColor?: string;
} }
export interface Song { export interface Song {
+37 -3
View File
@@ -1,6 +1,40 @@
export const getImageLinearBackground = async (imageSrc: string): Promise<string> => { export const getImageLinearBackground = async (imageSrc: string): Promise<string> => {
const primaryColor = await getImagePrimaryColor(imageSrc); try {
return generateGradientBackground(primaryColor); const primaryColor = await getImagePrimaryColor(imageSrc);
return generateGradientBackground(primaryColor);
} catch (error) {
console.error('error', error);
return '';
}
};
export const getImageBackground = async (img: HTMLImageElement): Promise<string> => {
try {
const primaryColor = await getImageColor(img);
return generateGradientBackground(primaryColor);
} catch (error) {
console.error('error', error);
return '';
}
};
const getImageColor = (img: HTMLImageElement): Promise<string> => {
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
reject(new Error('无法获取canvas上下文'));
return;
}
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const color = getAverageColor(imageData.data);
resolve(`rgb(${color.join(',')})`);
});
}; };
const getImagePrimaryColor = (imageSrc: string): Promise<string> => { const getImagePrimaryColor = (imageSrc: string): Promise<string> => {
@@ -49,7 +83,7 @@ const generateGradientBackground = (color: string): string => {
const [h, s, l] = rgbToHsl(r, g, b); const [h, s, l] = rgbToHsl(r, g, b);
// 增加亮度和暗度的差异 // 增加亮度和暗度的差异
const lightL = Math.min(l + 0.8, 0.95); const lightL = Math.min(l + 0.5, 0.95);
const darkL = Math.max(l - 0.5, 0.05); const darkL = Math.max(l - 0.5, 0.05);
const midL = (lightL + darkL) / 2; const midL = (lightL + darkL) / 2;