mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-28 10:57:23 +08:00
✨ feat: 优化歌词体验
This commit is contained in:
@@ -39,6 +39,7 @@ function createWindow() {
|
|||||||
label: '退出',
|
label: '退出',
|
||||||
click: () => {
|
click: () => {
|
||||||
win.destroy();
|
win.destroy();
|
||||||
|
app.quit();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
+35
-3
@@ -3,7 +3,7 @@ const path = require('path');
|
|||||||
|
|
||||||
let lyricWindow = null;
|
let lyricWindow = null;
|
||||||
|
|
||||||
const loadLyricWindow = (ipcMain) => {
|
const createWin = () => {
|
||||||
lyricWindow = new BrowserWindow({
|
lyricWindow = new BrowserWindow({
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 300,
|
height: 300,
|
||||||
@@ -16,8 +16,16 @@ const loadLyricWindow = (ipcMain) => {
|
|||||||
contextIsolation: false,
|
contextIsolation: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
const loadLyricWindow = (ipcMain) => {
|
||||||
ipcMain.on('open-lyric', () => {
|
ipcMain.on('open-lyric', () => {
|
||||||
|
if (lyricWindow) {
|
||||||
|
if (lyricWindow.isMinimized()) lyricWindow.restore();
|
||||||
|
lyricWindow.focus();
|
||||||
|
lyricWindow.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createWin();
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
lyricWindow.webContents.openDevTools({ mode: 'detach' });
|
lyricWindow.webContents.openDevTools({ mode: 'detach' });
|
||||||
lyricWindow.loadURL('http://localhost:4678/#/lyric');
|
lyricWindow.loadURL('http://localhost:4678/#/lyric');
|
||||||
@@ -26,11 +34,35 @@ const loadLyricWindow = (ipcMain) => {
|
|||||||
lyricWindow.loadURL(`file://${distPath}/index.html#/lyric`);
|
lyricWindow.loadURL(`file://${distPath}/index.html#/lyric`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lyricWindow.setMinimumSize(600, 200);
|
||||||
|
|
||||||
|
// 隐藏任务栏
|
||||||
|
lyricWindow.setSkipTaskbar(true);
|
||||||
|
|
||||||
lyricWindow.show();
|
lyricWindow.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('send-lyric', (e, data) => {
|
ipcMain.on('send-lyric', (e, data) => {
|
||||||
lyricWindow.webContents.send('receive-lyric', data);
|
if (lyricWindow) {
|
||||||
|
lyricWindow.webContents.send('receive-lyric', data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('top-lyric', (e, data) => {
|
||||||
|
lyricWindow.setAlwaysOnTop(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('close-lyric', () => {
|
||||||
|
lyricWindow.close();
|
||||||
|
lyricWindow = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('mouseenter-lyric', () => {
|
||||||
|
lyricWindow.setIgnoreMouseEvents(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('mouseleave-lyric', () => {
|
||||||
|
lyricWindow.setIgnoreMouseEvents(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -39,8 +39,8 @@
|
|||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
"@vue/compiler-sfc": "^3.3.4",
|
"@vue/compiler-sfc": "^3.3.4",
|
||||||
"@vue/eslint-config-typescript": "^12.0.0",
|
"@vue/eslint-config-typescript": "^12.0.0",
|
||||||
"electron": "^28.0.0",
|
"electron": "^30.0.6",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.13.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
<i class="iconfont icon-likefill"></i>
|
<i class="iconfont icon-likefill"></i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="song-item-operating-play bg-black"
|
class="song-item-operating-play bg-black animate__animated"
|
||||||
:class="isPlaying ? 'bg-green-600' : ''"
|
:class="{ 'bg-green-600': isPlaying, animate__flipInY: playLoading }"
|
||||||
@click="playMusicEvent(item)"
|
@click="playMusicEvent(item)"
|
||||||
>
|
>
|
||||||
<i v-if="isPlaying && play" class="iconfont icon-stop"></i>
|
<i v-if="isPlaying && play" class="iconfont icon-stop"></i>
|
||||||
@@ -51,6 +51,8 @@ const play = computed(() => store.state.play as boolean);
|
|||||||
|
|
||||||
const playMusic = computed(() => store.state.playMusic);
|
const playMusic = computed(() => store.state.playMusic);
|
||||||
|
|
||||||
|
const playLoading = computed(() => playMusic.value.id === props.item.id && playMusic.value.playLoading);
|
||||||
|
|
||||||
// 判断是否为正在播放的音乐
|
// 判断是否为正在播放的音乐
|
||||||
const isPlaying = computed(() => {
|
const isPlaying = computed(() => {
|
||||||
return playMusic.value.id === props.item.id;
|
return playMusic.value.id === props.item.id;
|
||||||
@@ -59,8 +61,11 @@ const isPlaying = computed(() => {
|
|||||||
const emits = defineEmits(['play']);
|
const emits = defineEmits(['play']);
|
||||||
|
|
||||||
// 播放音乐 设置音乐详情 打开音乐底栏
|
// 播放音乐 设置音乐详情 打开音乐底栏
|
||||||
const playMusicEvent = (item: any) => {
|
const playMusicEvent = async (item: any) => {
|
||||||
store.commit('setPlay', item);
|
if (playMusic.value.id === item.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await store.commit('setPlay', item);
|
||||||
store.commit('setIsPlay', true);
|
store.commit('setIsPlay', true);
|
||||||
emits('play', item);
|
emits('play', item);
|
||||||
};
|
};
|
||||||
@@ -105,6 +110,7 @@ const playMusicEvent = (item: any) => {
|
|||||||
}
|
}
|
||||||
&-play {
|
&-play {
|
||||||
@apply cursor-pointer border border-gray-500 rounded-full w-10 h-10 flex justify-center items-center hover:bg-green-600 transition;
|
@apply cursor-pointer border border-gray-500 rounded-full w-10 h-10 flex justify-center items-center hover:bg-green-600 transition;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export const getLrcTimeRange = (index: any) => {
|
|||||||
return { currentTime, nextTime };
|
return { currentTime, nextTime };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sendLyricToWin = (isPlay: boolean) => {
|
export const sendLyricToWin = (isPlay: boolean = true) => {
|
||||||
try {
|
try {
|
||||||
// 设置lyricWinData 获取 当前播放的两句歌词 和歌词时间
|
// 设置lyricWinData 获取 当前播放的两句歌词 和歌词时间
|
||||||
let lyricWinData = null;
|
let lyricWinData = null;
|
||||||
@@ -166,4 +166,5 @@ export const sendLyricToWin = (isPlay: boolean) => {
|
|||||||
export const openLyric = () => {
|
export const openLyric = () => {
|
||||||
const windowData = window as any;
|
const windowData = window as any;
|
||||||
windowData.electronAPI.openLyric();
|
windowData.electronAPI.openLyric();
|
||||||
|
sendLyricToWin();
|
||||||
};
|
};
|
||||||
|
|||||||
+4
-1
@@ -16,6 +16,7 @@ interface State {
|
|||||||
playList: SongResult[];
|
playList: SongResult[];
|
||||||
playListIndex: number;
|
playListIndex: number;
|
||||||
setData: any;
|
setData: any;
|
||||||
|
lyric: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state: State = {
|
const state: State = {
|
||||||
@@ -28,6 +29,7 @@ const state: State = {
|
|||||||
playList: [],
|
playList: [],
|
||||||
playListIndex: 0,
|
playListIndex: 0,
|
||||||
setData: null,
|
setData: null,
|
||||||
|
lyric: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const windowData = window as any;
|
const windowData = window as any;
|
||||||
@@ -39,10 +41,11 @@ const mutations = {
|
|||||||
state.menus = menus;
|
state.menus = menus;
|
||||||
},
|
},
|
||||||
async setPlay(state: State, playMusic: SongResult) {
|
async setPlay(state: State, playMusic: SongResult) {
|
||||||
state.playMusic = playMusic;
|
state.playMusic = { ...playMusic, playLoading: true };
|
||||||
state.playMusicUrl = await getSongUrl(playMusic.id);
|
state.playMusicUrl = await getSongUrl(playMusic.id);
|
||||||
state.play = true;
|
state.play = true;
|
||||||
musicHistory.addMusic(playMusic);
|
musicHistory.addMusic(playMusic);
|
||||||
|
state.playMusic.playLoading = false;
|
||||||
},
|
},
|
||||||
setIsPlay(state: State, isPlay: boolean) {
|
setIsPlay(state: State, isPlay: boolean) {
|
||||||
state.isPlay = isPlay;
|
state.isPlay = isPlay;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export interface SongResult {
|
|||||||
song: Song;
|
song: Song;
|
||||||
alg: string;
|
alg: string;
|
||||||
count?: number;
|
count?: number;
|
||||||
|
playLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Song {
|
interface Song {
|
||||||
|
|||||||
+118
-30
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="lyric-window" :class="theme">
|
<div class="lyric-window" :class="[lyricSetting.theme, { lyric_lock: lyricSetting.isLock }]">
|
||||||
|
<div class="drag-bar"></div>
|
||||||
<div class="lyric-bar" :class="{ 'lyric-bar-hover': isDrag }">
|
<div class="lyric-bar" :class="{ 'lyric-bar-hover': isDrag }">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<!-- <div class="music-buttons">
|
<!-- <div class="music-buttons">
|
||||||
@@ -13,23 +14,32 @@
|
|||||||
<i class="iconfont icon-next"></i>
|
<i class="iconfont icon-next"></i>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="check-theme" @click="checkTheme">
|
<div class="button check-theme" @click="checkTheme">
|
||||||
<i v-if="theme === 'light'" class="icon ri-sun-line"></i>
|
<i v-if="lyricSetting.theme === 'light'" class="icon ri-sun-line"></i>
|
||||||
<i v-else class="icon ri-moon-line"></i>
|
<i v-else class="icon ri-moon-line"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-move">
|
<div class="button">
|
||||||
<i class="icon ri-drag-move-2-line"></i>
|
<i class="icon ri-share-2-line" :class="{ checked: lyricSetting.isTop }" @click="handleTop"></i>
|
||||||
|
</div>
|
||||||
|
<div class="button button-lock" @click="handleLock">
|
||||||
|
<i v-if="lyricSetting.isLock" class="icon ri-lock-line"></i>
|
||||||
|
<i v-else class="icon ri-lock-unlock-line"></i>
|
||||||
|
</div>
|
||||||
|
<div class="button">
|
||||||
|
<i class="icon ri-close-circle-line" @click="handleClose"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="lyricData.lrcArray[lyricData.nowIndex]" class="lyric-box">
|
<div id="clickThroughElement" class="lyric-box">
|
||||||
<h2 class="lyric lyric-current">{{ lyricData.lrcArray[lyricData.nowIndex].text }}</h2>
|
<template v-if="lyricData.lrcArray[lyricData.nowIndex]">
|
||||||
<p class="lyric-current">{{ lyricData.currentLrc.trText }}</p>
|
<h2 class="lyric lyric-current">{{ lyricData.lrcArray[lyricData.nowIndex].text }}</h2>
|
||||||
<template v-if="lyricData.lrcArray[lyricData.nowIndex + 1]">
|
<p class="lyric-current">{{ lyricData.currentLrc.trText }}</p>
|
||||||
<h2 class="lyric lyric-next">
|
<template v-if="lyricData.lrcArray[lyricData.nowIndex + 1]">
|
||||||
{{ lyricData.lrcArray[lyricData.nowIndex + 1].text }}
|
<h2 class="lyric lyric-next">
|
||||||
</h2>
|
{{ lyricData.lrcArray[lyricData.nowIndex + 1].text }}
|
||||||
<p class="lyric-next">{{ lyricData.nextLrc.trText }}</p>
|
</h2>
|
||||||
|
<p class="lyric-next">{{ lyricData.nextLrc.trText }}</p>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,7 +48,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useIpcRenderer } from '@vueuse/electron';
|
import { useIpcRenderer } from '@vueuse/electron';
|
||||||
|
|
||||||
const windowData = window as any;
|
|
||||||
const ipcRenderer = useIpcRenderer();
|
const ipcRenderer = useIpcRenderer();
|
||||||
|
|
||||||
const lyricData = ref({
|
const lyricData = ref({
|
||||||
@@ -60,27 +69,64 @@ const lyricData = ref({
|
|||||||
nowIndex: 0,
|
nowIndex: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const lyricSetting = ref({
|
||||||
|
...(localStorage.getItem('lyricData')
|
||||||
|
? JSON.parse(localStorage.getItem('lyricData') || '')
|
||||||
|
: {
|
||||||
|
isTop: false,
|
||||||
|
theme: 'light',
|
||||||
|
isLock: false,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
ipcRenderer.on('receive-lyric', (event, data) => {
|
ipcRenderer.on('receive-lyric', (event, data) => {
|
||||||
try {
|
try {
|
||||||
lyricData.value = JSON.parse(data);
|
lyricData.value = JSON.parse(data);
|
||||||
console.log('lyricData.value', lyricData.value);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('error', error);
|
console.error('error', error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const theme = ref('dark');
|
|
||||||
const checkTheme = () => {
|
const checkTheme = () => {
|
||||||
if (theme.value === 'light') {
|
if (lyricSetting.value.theme === 'light') {
|
||||||
theme.value = 'dark';
|
lyricSetting.value.theme = 'dark';
|
||||||
} else {
|
} else {
|
||||||
theme.value = 'light';
|
lyricSetting.value.theme = 'light';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// const drag = (event: MouseEvent) => {
|
|
||||||
// windowData.electronAPI.dragStart(event);
|
const handleTop = () => {
|
||||||
// };
|
lyricSetting.value.isTop = !lyricSetting.value.isTop;
|
||||||
|
ipcRenderer.send('top-lyric', lyricSetting.value.isTop);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLock = () => {
|
||||||
|
lyricSetting.value.isLock = !lyricSetting.value.isLock;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
ipcRenderer.send('close-lyric');
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => lyricSetting.value,
|
||||||
|
(newValue) => {
|
||||||
|
localStorage.setItem('lyricData', JSON.stringify(newValue));
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// onMounted(() => {
|
||||||
|
// const el = document.getElementById('clickThroughElement') as HTMLElement;
|
||||||
|
// el.addEventListener('mouseenter', () => {
|
||||||
|
// if (lyricSetting.value.isLock) ipcRenderer.send('mouseenter-lyric');
|
||||||
|
// });
|
||||||
|
// el.addEventListener('mouseleave', () => {
|
||||||
|
// if (lyricSetting.value.isLock) ipcRenderer.send('mouseleave-lyric');
|
||||||
|
// });
|
||||||
|
// });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -93,10 +139,36 @@ body {
|
|||||||
.lyric-window {
|
.lyric-window {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@apply overflow-hidden text-gray-600 hover:bg-gray-400 hover:bg-opacity-75;
|
@apply overflow-hidden text-gray-600 rounded-xl box-border;
|
||||||
|
// border: 4px solid transparent;
|
||||||
&:hover .lyric-bar {
|
&:hover .lyric-bar {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
&:hover .drag-bar {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
box-shadow: inset 0 0 10px 0 rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lyric_lock {
|
||||||
|
&:hover {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
&:hover .lyric-bar {
|
||||||
|
background-color: transparent;
|
||||||
|
.button {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.button-lock {
|
||||||
|
opacity: 1;
|
||||||
|
color: #d6d6d6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover .drag-bar {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@@ -107,7 +179,7 @@ body {
|
|||||||
background-color: #b1b1b1;
|
background-color: #b1b1b1;
|
||||||
@apply flex flex-col justify-center items-center;
|
@apply flex flex-col justify-center items-center;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100px;
|
height: 40px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -116,10 +188,24 @@ body {
|
|||||||
.lyric-bar-hover {
|
.lyric-bar-hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drag-bar {
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
height: 20px;
|
||||||
|
cursor: move;
|
||||||
|
background-color: #383838;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
.buttons {
|
.buttons {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
@apply flex justify-center items-center;
|
@apply flex justify-center items-center gap-4;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
@apply cursor-pointer text-center;
|
||||||
|
}
|
||||||
|
.checked {
|
||||||
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
.button-move {
|
.button-move {
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
@@ -151,9 +237,9 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lyric {
|
.lyric {
|
||||||
text-shadow: 0 0 10px #fff;
|
text-shadow: 0 0 1vw #2c2c2c;
|
||||||
font-size: 4vw;
|
font-size: 4vw;
|
||||||
@apply font-bold m-0 p-0 whitespace-nowrap select-none pointer-events-none;
|
@apply font-bold m-0 p-0 select-none pointer-events-none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lyric-current {
|
.lyric-current {
|
||||||
@@ -168,6 +254,7 @@ body {
|
|||||||
.lyric-window.dark {
|
.lyric-window.dark {
|
||||||
.lyric {
|
.lyric {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
|
text-shadow: 0 0 1vw #000000;
|
||||||
}
|
}
|
||||||
.lyric-current {
|
.lyric-current {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -176,7 +263,8 @@ body {
|
|||||||
color: #cecece;
|
color: #cecece;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .lyric-box {
|
.lyric-box {
|
||||||
// writing-mode: vertical-rl;
|
// writing-mode: vertical-rl;
|
||||||
// }
|
padding: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user