mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-23 23:57:22 +08:00
✨ feat: 优化播放 修改为howler 修复搜索导致播放无限卡顿问题(#15)
- 优化了整个项目的播放 - 去除audio - 优化歌词页 歌词同步时间 fixes #15
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# 你的接口地址 (必填)
|
# 你的接口地址 (必填)
|
||||||
VITE_API = ***
|
VITE_API_LOCAL = ***
|
||||||
# 音乐破解接口地址
|
# 音乐破解接口地址
|
||||||
VITE_API_MUSIC = ***
|
VITE_API_MUSIC = ***
|
||||||
# 代理地址
|
# 代理地址
|
||||||
|
|||||||
@@ -68,23 +68,21 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# .env.development
|
# .env.development
|
||||||
# 你的接口地址 (必填)
|
VITE_API_LOCAL = /api
|
||||||
VITE_API = ***
|
|
||||||
# 音乐破解接口地址
|
|
||||||
VITE_API_MUSIC = ***
|
|
||||||
# 代理地址
|
|
||||||
VITE_API_PROXY = ***
|
|
||||||
|
|
||||||
|
|
||||||
# 本地运行代理地址
|
|
||||||
VITE_API_PROXY = /api
|
|
||||||
VITE_API_MUSIC_PROXY = /music
|
VITE_API_MUSIC_PROXY = /music
|
||||||
VITE_API_PROXY_MUSIC = /music_proxy
|
VITE_API_PROXY_MUSIC = /music_proxy
|
||||||
|
|
||||||
|
# 你的接口地址 (必填)
|
||||||
|
VITE_API = ***
|
||||||
|
# 音乐po接口地址
|
||||||
|
VITE_API_MUSIC = ***
|
||||||
|
VITE_API_PROXY = ***
|
||||||
|
|
||||||
|
|
||||||
# .env.production
|
# .env.production
|
||||||
# 你的接口地址 (必填)
|
# 你的接口地址 (必填)
|
||||||
VITE_API = ***
|
VITE_API = ***
|
||||||
# 音乐破解接口地址
|
# 音乐po接口地址
|
||||||
VITE_API_MUSIC = ***
|
VITE_API_MUSIC = ***
|
||||||
# 代理地址
|
# 代理地址
|
||||||
VITE_API_PROXY = ***
|
VITE_API_PROXY = ***
|
||||||
|
|||||||
+3
-1
@@ -17,7 +17,9 @@
|
|||||||
"b:win": "cross-env NODE_ENV=production npm run build && npm run b:win:x64 && npm run b:win:x86 && npm run b:win:arm"
|
"b:win": "cross-env NODE_ENV=production npm run build && npm run b:win:x64 && npm run b:win:x86 && npm run b:win:arm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-store": "^8.1.0"
|
"@types/howler": "^2.2.12",
|
||||||
|
"electron-store": "^8.1.0",
|
||||||
|
"howler": "^2.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss7-compat": "^2.2.4",
|
"@tailwindcss/postcss7-compat": "^2.2.4",
|
||||||
|
|||||||
+1
-5
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container" :class="{ mobile: isMobile }">
|
<div class="app-container" :class="{ mobile: isMobile }">
|
||||||
<audio id="MusicAudio" ref="audioRef" :src="playMusicUrl" :autoplay="play"></audio>
|
|
||||||
<n-config-provider :theme="darkTheme">
|
<n-config-provider :theme="darkTheme">
|
||||||
<n-dialog-provider>
|
<n-dialog-provider>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
@@ -11,15 +10,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { darkTheme } from 'naive-ui';
|
import { darkTheme } from 'naive-ui';
|
||||||
import { computed, onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
|
|
||||||
import { isMobile } from './utils';
|
import { isMobile } from './utils';
|
||||||
|
|
||||||
const playMusicUrl = computed(() => store.state.playMusicUrl as string);
|
|
||||||
const play = computed(() => store.state.play as boolean);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
store.dispatch('initializeSettings');
|
store.dispatch('initializeSettings');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ const loadData = async () => {
|
|||||||
const {
|
const {
|
||||||
data: { data: dayRecommend },
|
data: { data: dayRecommend },
|
||||||
} = await getDayRecommend();
|
} = await getDayRecommend();
|
||||||
console.log('dayRecommend', dayRecommend);
|
|
||||||
// 处理数据
|
// 处理数据
|
||||||
if (dayRecommend) {
|
if (dayRecommend) {
|
||||||
singerData.artists = singerData.artists.slice(0, 4);
|
singerData.artists = singerData.artists.slice(0, 4);
|
||||||
|
|||||||
+81
-33
@@ -1,5 +1,6 @@
|
|||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import { audioService } from '@/services/audioService';
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
import type { ILyricText, SongResult } from '@/type/music';
|
import type { ILyricText, SongResult } from '@/type/music';
|
||||||
|
|
||||||
@@ -14,8 +15,34 @@ export const allTime = ref(0); // 总播放时间
|
|||||||
export const nowIndex = ref(0); // 当前播放歌词
|
export const nowIndex = ref(0); // 当前播放歌词
|
||||||
export const correctionTime = ref(0.4); // 歌词矫正时间Correction time
|
export const correctionTime = ref(0.4); // 歌词矫正时间Correction time
|
||||||
export const currentLrcProgress = ref(0); // 来存储当前歌词的进度
|
export const currentLrcProgress = ref(0); // 来存储当前歌词的进度
|
||||||
export const audio = ref<HTMLAudioElement>(); // 音频对象
|
|
||||||
export const playMusic = computed(() => store.state.playMusic as SongResult); // 当前播放歌曲
|
export const playMusic = computed(() => store.state.playMusic as SongResult); // 当前播放歌曲
|
||||||
|
export const sound = ref<Howl | null>(audioService.getCurrentSound());
|
||||||
|
|
||||||
|
document.onkeyup = (e) => {
|
||||||
|
switch (e.code) {
|
||||||
|
case 'Space':
|
||||||
|
if (store.state.play) {
|
||||||
|
store.commit('setPlayMusic', false);
|
||||||
|
audioService.getCurrentSound()?.pause();
|
||||||
|
} else {
|
||||||
|
store.commit('setPlayMusic', true);
|
||||||
|
audioService.getCurrentSound()?.play();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => store.state.playMusicUrl,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
audioService.play(newVal);
|
||||||
|
sound.value = audioService.getCurrentSound();
|
||||||
|
audioServiceOn(audioService);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => store.state.playMusic,
|
() => store.state.playMusic,
|
||||||
@@ -29,6 +56,48 @@ watch(
|
|||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const audioServiceOn = (audio: typeof audioService) => {
|
||||||
|
let interval: any = null;
|
||||||
|
|
||||||
|
// 监听播放
|
||||||
|
audio.onPlay(() => {
|
||||||
|
store.commit('setPlayMusic', true);
|
||||||
|
interval = setInterval(() => {
|
||||||
|
nowTime.value = sound.value?.seek() as number;
|
||||||
|
allTime.value = sound.value?.duration() as number;
|
||||||
|
const newIndex = getLrcIndex(nowTime.value);
|
||||||
|
if (newIndex !== nowIndex.value) {
|
||||||
|
nowIndex.value = newIndex;
|
||||||
|
currentLrcProgress.value = 0;
|
||||||
|
}
|
||||||
|
if (isElectron.value) {
|
||||||
|
sendLyricToWin();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听暂停
|
||||||
|
audio.onPause(() => {
|
||||||
|
store.commit('setPlayMusic', false);
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听结束
|
||||||
|
audio.onEnd(() => {
|
||||||
|
handleEnded();
|
||||||
|
store.commit('nextPlay');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const play = () => {
|
||||||
|
audioService.getCurrentSound()?.play();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pause = () => {
|
||||||
|
audioService.getCurrentSound()?.pause();
|
||||||
|
};
|
||||||
|
|
||||||
const isPlaying = computed(() => store.state.play as boolean);
|
const isPlaying = computed(() => store.state.play as boolean);
|
||||||
|
|
||||||
// 增加矫正时间
|
// 增加矫正时间
|
||||||
@@ -78,25 +147,18 @@ export const getLrcStyle = (index: number) => {
|
|||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(nowTime, (newTime) => {
|
|
||||||
const newIndex = getLrcIndex(newTime);
|
|
||||||
if (newIndex !== nowIndex.value) {
|
|
||||||
nowIndex.value = newIndex;
|
|
||||||
currentLrcProgress.value = 0; // 重置进度
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 播放进度
|
// 播放进度
|
||||||
export const useLyricProgress = () => {
|
export const useLyricProgress = () => {
|
||||||
let animationFrameId: number | null = null;
|
let animationFrameId: number | null = null;
|
||||||
|
|
||||||
const updateProgress = () => {
|
const updateProgress = () => {
|
||||||
if (!isPlaying.value) return;
|
if (!isPlaying.value) return;
|
||||||
audio.value = audio.value || (document.querySelector('#MusicAudio') as HTMLAudioElement);
|
const currentSound = sound.value;
|
||||||
if (!audio.value) return;
|
if (!currentSound) return;
|
||||||
|
|
||||||
const { start, end } = currentLrcTiming.value;
|
const { start, end } = currentLrcTiming.value;
|
||||||
const duration = end - start;
|
const duration = end - start;
|
||||||
const elapsed = audio.value.currentTime - start;
|
const elapsed = (currentSound.seek() as number) - start;
|
||||||
currentLrcProgress.value = Math.min(Math.max((elapsed / duration) * 100, 0), 100);
|
currentLrcProgress.value = Math.min(Math.max((elapsed / duration) * 100, 0), 100);
|
||||||
|
|
||||||
animationFrameId = requestAnimationFrame(updateProgress);
|
animationFrameId = requestAnimationFrame(updateProgress);
|
||||||
@@ -140,9 +202,12 @@ export const useLyricProgress = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 设置当前播放时间
|
// 设置当前播放时间
|
||||||
export const setAudioTime = (index: number, audio: HTMLAudioElement) => {
|
export const setAudioTime = (index: number) => {
|
||||||
audio.currentTime = lrcTimeArray.value[index];
|
const currentSound = sound.value;
|
||||||
audio.play();
|
if (!currentSound) return;
|
||||||
|
|
||||||
|
currentSound.seek(lrcTimeArray.value[index]);
|
||||||
|
currentSound.play();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取当前播放的歌词
|
// 获取当前播放的歌词
|
||||||
@@ -154,7 +219,7 @@ export const getCurrentLrc = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取一句歌词播放时间是 几秒到几秒
|
// 获取一句歌词播放时间几秒到几秒
|
||||||
export const getLrcTimeRange = (index: number) => ({
|
export const getLrcTimeRange = (index: number) => ({
|
||||||
currentTime: lrcTimeArray.value[index],
|
currentTime: lrcTimeArray.value[index],
|
||||||
nextTime: lrcTimeArray.value[index + 1],
|
nextTime: lrcTimeArray.value[index + 1],
|
||||||
@@ -180,26 +245,9 @@ watch(isPlaying, (newIsPlaying) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听时间变化
|
|
||||||
watch(nowTime, (newTime) => {
|
|
||||||
const newIndex = getLrcIndex(newTime);
|
|
||||||
if (newIndex !== nowIndex.value) {
|
|
||||||
nowIndex.value = newIndex;
|
|
||||||
currentLrcProgress.value = 0; // 重置进度
|
|
||||||
// 当索引变化时发送更新
|
|
||||||
if (isElectron.value) {
|
|
||||||
sendLyricToWin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 处理歌曲结束
|
// 处理歌曲结束
|
||||||
export const handleEnded = () => {
|
export const handleEnded = () => {
|
||||||
// ... 原有的结束处理逻辑 ...
|
|
||||||
|
|
||||||
// 如果有歌词窗口,发送初始化数据
|
|
||||||
if (isElectron.value) {
|
if (isElectron.value) {
|
||||||
// 延迟一下等待新歌曲加载完成
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
initLyricWindow();
|
initLyricWindow();
|
||||||
sendLyricToWin();
|
sendLyricToWin();
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
import { Howl } from 'howler';
|
||||||
|
|
||||||
import { getMusicLrc, getMusicUrl, getParsingMusicUrl } from '@/api/music';
|
import { getMusicLrc, getMusicUrl, getParsingMusicUrl } from '@/api/music';
|
||||||
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
|
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
|
||||||
|
import { audioService } from '@/services/audioService';
|
||||||
import type { ILyric, ILyricText, SongResult } from '@/type/music';
|
import type { ILyric, ILyricText, SongResult } from '@/type/music';
|
||||||
import { getImgUrl, getMusicProxyUrl } from '@/utils';
|
import { getImgUrl, getMusicProxyUrl } from '@/utils';
|
||||||
import { getImageLinearBackground } from '@/utils/linearColor';
|
import { getImageLinearBackground } from '@/utils/linearColor';
|
||||||
@@ -53,9 +56,13 @@ export const useMusicListHook = () => {
|
|||||||
|
|
||||||
// 用于预加载下一首歌曲的 MP3 数据
|
// 用于预加载下一首歌曲的 MP3 数据
|
||||||
const preloadNextSong = (nextSongUrl: string) => {
|
const preloadNextSong = (nextSongUrl: string) => {
|
||||||
const audio = new Audio(nextSongUrl);
|
const sound = new Howl({
|
||||||
audio.preload = 'auto'; // 设置预加载
|
src: [nextSongUrl],
|
||||||
audio.load(); // 手动加载
|
html5: true,
|
||||||
|
preload: true,
|
||||||
|
autoplay: false,
|
||||||
|
});
|
||||||
|
return sound;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchSongs = async (state: any, startIndex: number, endIndex: number) => {
|
const fetchSongs = async (state: any, startIndex: number, endIndex: number) => {
|
||||||
@@ -166,9 +173,19 @@ export const useMusicListHook = () => {
|
|||||||
state.playMusic.lyric = lyrics;
|
state.playMusic.lyric = lyrics;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const play = () => {
|
||||||
|
audioService.getCurrentSound()?.play();
|
||||||
|
};
|
||||||
|
|
||||||
|
const pause = () => {
|
||||||
|
audioService.getCurrentSound()?.pause();
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handlePlayMusic,
|
handlePlayMusic,
|
||||||
nextPlay,
|
nextPlay,
|
||||||
prevPlay,
|
prevPlay,
|
||||||
|
play,
|
||||||
|
pause,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,68 +64,9 @@ const store = useStore();
|
|||||||
|
|
||||||
const isPlay = computed(() => store.state.isPlay as boolean);
|
const isPlay = computed(() => store.state.isPlay as boolean);
|
||||||
const { menus } = store.state;
|
const { menus } = store.state;
|
||||||
const play = computed(() => store.state.play as boolean);
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const audio = {
|
|
||||||
value: document.querySelector('#MusicAudio') as HTMLAudioElement,
|
|
||||||
};
|
|
||||||
|
|
||||||
const backgroundColor = ref('#000');
|
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(
|
|
||||||
() => play.value,
|
|
||||||
(value) => {
|
|
||||||
if (value && audio.value) {
|
|
||||||
audioPlay();
|
|
||||||
} else {
|
|
||||||
audioPause();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
document.onkeyup = (e) => {
|
|
||||||
switch (e.code) {
|
|
||||||
case 'Space':
|
|
||||||
playMusicEvent();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const audioPlay = () => {
|
|
||||||
if (audio.value) {
|
|
||||||
audio.value.play();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const audioPause = () => {
|
|
||||||
if (audio.value) {
|
|
||||||
audio.value.pause();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const playMusicEvent = async () => {
|
|
||||||
if (play.value) {
|
|
||||||
store.commit('setPlayMusic', false);
|
|
||||||
} else {
|
|
||||||
store.commit('setPlayMusic', true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
class="music-lrc-text"
|
class="music-lrc-text"
|
||||||
:class="{ 'now-text': index === nowIndex, 'hover-text': item.text }"
|
:class="{ 'now-text': index === nowIndex, 'hover-text': item.text }"
|
||||||
@click="setAudioTime(index, audio)"
|
@click="setAudioTime(index)"
|
||||||
>
|
>
|
||||||
<span :style="getLrcStyle(index)">{{ item.text }}</span>
|
<span :style="getLrcStyle(index)">{{ item.text }}</span>
|
||||||
<div class="music-lrc-text-tr">{{ item.trText }}</div>
|
<div class="music-lrc-text-tr">{{ item.trText }}</div>
|
||||||
@@ -75,10 +75,6 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
audio: {
|
|
||||||
type: HTMLAudioElement,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
background: {
|
background: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
@@ -260,6 +256,7 @@ defineExpose({
|
|||||||
span {
|
span {
|
||||||
background-clip: text !important;
|
background-clip: text !important;
|
||||||
-webkit-background-clip: text !important;
|
-webkit-background-clip: text !important;
|
||||||
|
padding-right: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-tr {
|
&-tr {
|
||||||
@@ -291,6 +288,9 @@ defineExpose({
|
|||||||
.music-lrc {
|
.music-lrc {
|
||||||
height: calc(100vh - 260px) !important;
|
height: calc(100vh - 260px) !important;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
span {
|
||||||
|
padding-right: 0px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.music-lrc-text {
|
.music-lrc-text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 展开全屏 -->
|
<!-- 展开全屏 -->
|
||||||
<music-full
|
<music-full ref="MusicFullRef" v-model:music-full="musicFullVisible" :background="background" />
|
||||||
ref="MusicFullRef"
|
|
||||||
v-model:music-full="musicFullVisible"
|
|
||||||
:audio="audio.value as HTMLAudioElement"
|
|
||||||
:background="background"
|
|
||||||
/>
|
|
||||||
<!-- 底部播放栏 -->
|
<!-- 底部播放栏 -->
|
||||||
<div
|
<div
|
||||||
class="music-play-bar"
|
class="music-play-bar"
|
||||||
@@ -112,11 +107,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useThrottleFn } from '@vueuse/core';
|
||||||
import { useTemplateRef } from 'vue';
|
import { useTemplateRef } from 'vue';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
import SongItem from '@/components/common/SongItem.vue';
|
import SongItem from '@/components/common/SongItem.vue';
|
||||||
import { allTime, getCurrentLrc, isElectron, nowTime, openLyric, sendLyricToWin } from '@/hooks/MusicHook';
|
import { allTime, isElectron, nowTime, openLyric, sound } from '@/hooks/MusicHook';
|
||||||
import type { SongResult } from '@/type/music';
|
import type { SongResult } from '@/type/music';
|
||||||
import { getImgUrl, secondToMinute, setAnimationClass } from '@/utils';
|
import { getImgUrl, secondToMinute, setAnimationClass } from '@/utils';
|
||||||
|
|
||||||
@@ -128,12 +124,8 @@ const store = useStore();
|
|||||||
const playMusic = computed(() => store.state.playMusic as SongResult);
|
const playMusic = computed(() => store.state.playMusic as SongResult);
|
||||||
// 是否播放
|
// 是否播放
|
||||||
const play = computed(() => store.state.play as boolean);
|
const play = computed(() => store.state.play as boolean);
|
||||||
|
|
||||||
const playList = computed(() => store.state.playList as SongResult[]);
|
const playList = computed(() => store.state.playList as SongResult[]);
|
||||||
|
|
||||||
const audio = {
|
|
||||||
value: document.querySelector('#MusicAudio') as HTMLAudioElement,
|
|
||||||
};
|
|
||||||
const background = ref('#000');
|
const background = ref('#000');
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -144,30 +136,28 @@ watch(
|
|||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const audioPlay = () => {
|
// 使用 useThrottleFn 创建节流版本的 seek 函数
|
||||||
if (audio.value) {
|
const throttledSeek = useThrottleFn((value: number) => {
|
||||||
audio.value.play();
|
if (!sound.value) return;
|
||||||
}
|
sound.value.seek((value * allTime.value) / 100);
|
||||||
};
|
store.commit('setPlayMusic', true);
|
||||||
|
}, 50); // 50ms 的节流延迟
|
||||||
|
|
||||||
// 计算属性 获取当前播放时间的进度
|
// 修改 timeSlider 计算属性
|
||||||
const timeSlider = computed({
|
const timeSlider = computed({
|
||||||
get: () => (nowTime.value / allTime.value) * 100,
|
get: () => (nowTime.value / allTime.value) * 100,
|
||||||
set: (value) => {
|
set: throttledSeek,
|
||||||
if (!audio.value) return;
|
|
||||||
audio.value.currentTime = (value * allTime.value) / 100;
|
|
||||||
audioPlay();
|
|
||||||
store.commit('setPlayMusic', true);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 音量条
|
// 音量条
|
||||||
const audioVolume = ref(1);
|
const audioVolume = ref(localStorage.getItem('volume') ? parseFloat(localStorage.getItem('volume') as string) : 1);
|
||||||
const volumeSlider = computed({
|
const volumeSlider = computed({
|
||||||
get: () => audioVolume.value * 100,
|
get: () => audioVolume.value * 100,
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
if (!audio.value) return;
|
if (!sound.value) return;
|
||||||
audio.value.volume = value / 100;
|
localStorage.setItem('volume', (value / 100).toString());
|
||||||
|
sound.value.volume(value / 100);
|
||||||
|
audioVolume.value = value / 100;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// 获取当前播放时间
|
// 获取当前播放时间
|
||||||
@@ -180,25 +170,6 @@ const getAllTime = computed(() => {
|
|||||||
return secondToMinute(allTime.value);
|
return secondToMinute(allTime.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听音乐播放 获取时间
|
|
||||||
const onAudio = () => {
|
|
||||||
if (audio.value) {
|
|
||||||
audio.value.removeEventListener('timeupdate', handleGetAudioTime);
|
|
||||||
audio.value.removeEventListener('ended', handleEnded);
|
|
||||||
audio.value.addEventListener('timeupdate', handleGetAudioTime);
|
|
||||||
audio.value.addEventListener('ended', handleEnded);
|
|
||||||
// 监听音乐播放暂停
|
|
||||||
audio.value.addEventListener('pause', () => {
|
|
||||||
store.commit('setPlayMusic', false);
|
|
||||||
});
|
|
||||||
audio.value.addEventListener('play', () => {
|
|
||||||
store.commit('setPlayMusic', true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onAudio();
|
|
||||||
|
|
||||||
function handleEnded() {
|
function handleEnded() {
|
||||||
store.commit('nextPlay');
|
store.commit('nextPlay');
|
||||||
}
|
}
|
||||||
@@ -209,27 +180,17 @@ function handlePrev() {
|
|||||||
|
|
||||||
const MusicFullRef = ref<any>(null);
|
const MusicFullRef = ref<any>(null);
|
||||||
|
|
||||||
function handleGetAudioTime(this: HTMLAudioElement) {
|
|
||||||
// 监听音频播放的实时时间事件
|
|
||||||
const audio = this as HTMLAudioElement;
|
|
||||||
// 获取当前播放时间
|
|
||||||
nowTime.value = audio.currentTime;
|
|
||||||
getCurrentLrc();
|
|
||||||
// 获取总时间
|
|
||||||
allTime.value = audio.duration;
|
|
||||||
// 获取音量
|
|
||||||
audioVolume.value = audio.volume;
|
|
||||||
sendLyricToWin(store.state.isPlay);
|
|
||||||
// if (musicFullVisible.value) {
|
|
||||||
// MusicFullRef.value?.lrcScroll();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 播放暂停按钮事件
|
// 播放暂停按钮事件
|
||||||
const playMusicEvent = async () => {
|
const playMusicEvent = async () => {
|
||||||
if (play.value) {
|
if (play.value) {
|
||||||
|
if (sound.value) {
|
||||||
|
sound.value.pause();
|
||||||
|
}
|
||||||
store.commit('setPlayMusic', false);
|
store.commit('setPlayMusic', false);
|
||||||
} else {
|
} else {
|
||||||
|
if (sound.value) {
|
||||||
|
sound.value.play();
|
||||||
|
}
|
||||||
store.commit('setPlayMusic', true);
|
store.commit('setPlayMusic', true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { Howl } from 'howler';
|
||||||
|
|
||||||
|
class AudioService {
|
||||||
|
private currentSound: Howl | null = null;
|
||||||
|
|
||||||
|
play(url: string) {
|
||||||
|
if (this.currentSound) {
|
||||||
|
this.currentSound.unload();
|
||||||
|
}
|
||||||
|
this.currentSound = null;
|
||||||
|
this.currentSound = new Howl({
|
||||||
|
src: [url],
|
||||||
|
html5: true,
|
||||||
|
autoplay: true,
|
||||||
|
volume: localStorage.getItem('volume') ? parseFloat(localStorage.getItem('volume') as string) : 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.currentSound;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentSound() {
|
||||||
|
return this.currentSound;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (this.currentSound) {
|
||||||
|
this.currentSound.stop();
|
||||||
|
this.currentSound.unload();
|
||||||
|
this.currentSound = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听播放
|
||||||
|
onPlay(callback: () => void) {
|
||||||
|
if (this.currentSound) {
|
||||||
|
this.currentSound.on('play', callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听暂停
|
||||||
|
onPause(callback: () => void) {
|
||||||
|
if (this.currentSound) {
|
||||||
|
this.currentSound.on('pause', callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听结束
|
||||||
|
onEnd(callback: () => void) {
|
||||||
|
if (this.currentSound) {
|
||||||
|
this.currentSound.on('end', callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const audioService = new AudioService();
|
||||||
+2
-2
@@ -36,10 +36,10 @@ export default defineConfig({
|
|||||||
port: 4488,
|
port: 4488,
|
||||||
proxy: {
|
proxy: {
|
||||||
// with options
|
// with options
|
||||||
[process.env.VITE_API_PROXY as string]: {
|
[process.env.VITE_API_LOCAL as string]: {
|
||||||
target: process.env.VITE_API,
|
target: process.env.VITE_API,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(new RegExp(`^${process.env.VITE_API_PROXY}`), ''),
|
rewrite: (path) => path.replace(new RegExp(`^${process.env.VITE_API_LOCAL}`), ''),
|
||||||
},
|
},
|
||||||
[process.env.VITE_API_MUSIC_PROXY as string]: {
|
[process.env.VITE_API_MUSIC_PROXY as string]: {
|
||||||
target: process.env.VITE_API_MUSIC,
|
target: process.env.VITE_API_MUSIC,
|
||||||
|
|||||||
Reference in New Issue
Block a user