mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-18 18:20:49 +08:00
✨ feat: 优化歌词背景色 加载问题
This commit is contained in:
2
app.js
2
app.js
@@ -20,7 +20,7 @@ function createWindow() {
|
||||
win.setMinimumSize(1200, 780);
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
win.webContents.openDevTools({ mode: 'detach' });
|
||||
win.loadURL('http://localhost:4678/');
|
||||
win.loadURL('http://localhost:7788/');
|
||||
} else {
|
||||
win.loadURL(`file://${__dirname}/dist/index.html`);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
<template>
|
||||
<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-title">
|
||||
<n-ellipsis class="text-ellipsis" line-clamp="1">{{ item.name }}</n-ellipsis>
|
||||
@@ -30,10 +40,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useTemplateRef } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
|
||||
import type { SongResult } from '@/type/music';
|
||||
import { getImgUrl } from '@/utils';
|
||||
import { getImageBackground } from '@/utils/linearColor';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -60,6 +72,14 @@ const isPlaying = computed(() => {
|
||||
|
||||
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) => {
|
||||
if (playMusic.value.id === item.id) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="layout-page">
|
||||
<div class="layout-main">
|
||||
<div class="layout-main" :style="{ background: backgroundColor }">
|
||||
<title-bar v-if="isElectron" />
|
||||
<div class="layout-main-page" :class="isElectron ? '' : 'pt-6'">
|
||||
<!-- 侧边菜单栏 -->
|
||||
@@ -9,7 +9,7 @@
|
||||
<!-- 搜索栏 -->
|
||||
<search-bar />
|
||||
<!-- 主页面路由 -->
|
||||
<div class="main-content bg-black" :native-scrollbar="false">
|
||||
<div class="main-content" :native-scrollbar="false">
|
||||
<n-message-provider>
|
||||
<router-view
|
||||
v-slot="{ Component }"
|
||||
@@ -70,6 +70,18 @@ const audio = {
|
||||
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(() => {
|
||||
// 监听音乐是否播放
|
||||
watch(
|
||||
@@ -118,11 +130,11 @@ const playMusicEvent = async () => {
|
||||
.layout-page {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
@apply flex justify-center items-center overflow-hidden;
|
||||
@apply flex justify-center items-center overflow-hidden bg-black;
|
||||
}
|
||||
|
||||
.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%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -112,7 +112,7 @@ defineExpose({
|
||||
}
|
||||
.drawer-back {
|
||||
@apply absolute bg-cover bg-center;
|
||||
filter: brightness(80%);
|
||||
// filter: brightness(80%);
|
||||
z-index: -1;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
|
||||
@@ -127,7 +127,7 @@ watch(
|
||||
() => store.state.playMusic,
|
||||
async () => {
|
||||
loadLrc(playMusic.value.id);
|
||||
background.value = await getImageLinearBackground(getImgUrl(playMusic.value?.picUrl, '300y300'));
|
||||
background.value = playMusic.value.backgroundColor as string;
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
@@ -4,7 +4,8 @@ import { getMusicUrl, getParsingMusicUrl } from '@/api/music';
|
||||
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
|
||||
import homeRouter from '@/router/home';
|
||||
import type { SongResult } from '@/type/music';
|
||||
import { getMusicProxyUrl } from '@/utils';
|
||||
import { getImgUrl, getMusicProxyUrl } from '@/utils';
|
||||
import { getImageLinearBackground } from '@/utils/linearColor';
|
||||
|
||||
interface State {
|
||||
menus: any[];
|
||||
@@ -47,12 +48,7 @@ const mutations = {
|
||||
state.menus = menus;
|
||||
},
|
||||
async setPlay(state: State, playMusic: SongResult) {
|
||||
state.playMusic = { ...playMusic, playLoading: true };
|
||||
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);
|
||||
await getSongDetail(state, playMusic);
|
||||
},
|
||||
setIsPlay(state: State, isPlay: boolean) {
|
||||
state.isPlay = isPlay;
|
||||
@@ -69,16 +65,16 @@ const mutations = {
|
||||
state.play = true;
|
||||
return;
|
||||
}
|
||||
state.playListIndex = (state.playListIndex + 1) % state.playList.length;
|
||||
await updatePlayMusic(state);
|
||||
const playListIndex = (state.playListIndex + 1) % state.playList.length;
|
||||
await getSongDetail(state, state.playList[playListIndex]);
|
||||
},
|
||||
async prevPlay(state: State) {
|
||||
if (state.playList.length === 0) {
|
||||
state.play = true;
|
||||
return;
|
||||
}
|
||||
state.playListIndex = (state.playListIndex - 1 + state.playList.length) % state.playList.length;
|
||||
await updatePlayMusic(state);
|
||||
const playListIndex = (state.playListIndex - 1 + state.playList.length) % state.playList.length;
|
||||
await getSongDetail(state, state.playList[playListIndex]);
|
||||
},
|
||||
async setSetData(state: State, setData: any) {
|
||||
state.setData = setData;
|
||||
@@ -108,6 +104,20 @@ const updatePlayMusic = async (state: State) => {
|
||||
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({
|
||||
state,
|
||||
mutations,
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface SongResult {
|
||||
playLoading?: boolean;
|
||||
ar?: Artist[];
|
||||
al?: Album;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
|
||||
export interface Song {
|
||||
|
||||
@@ -1,6 +1,40 @@
|
||||
export const getImageLinearBackground = async (imageSrc: string): Promise<string> => {
|
||||
const primaryColor = await getImagePrimaryColor(imageSrc);
|
||||
return generateGradientBackground(primaryColor);
|
||||
try {
|
||||
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> => {
|
||||
@@ -49,7 +83,7 @@ const generateGradientBackground = (color: string): string => {
|
||||
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 midL = (lightL + darkL) / 2;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user