hookehuyr

fix(AudioPlayer): 增强音频播放控制的健壮性和错误处理

- 在loadAudio方法中添加对歌曲URL的检查,避免无效URL导致的错误
- 为所有播放控制方法添加try-catch块和状态检查
- 改进prevSong/nextSong/selectSong的流程,确保音频实例正确加载后再播放
- 添加更多错误日志帮助调试问题
- 完善播放状态管理,避免状态不一致
......@@ -157,21 +157,45 @@ onMounted(() => {
// 核心方法:音频加载
const loadAudio = async () => {
if (!currentSong.value) return
// 如果没有当前歌曲,直接返回
if (!currentSong.value) {
console.warn('没有可播放的歌曲')
return false
}
// 设置加载状态
isLoading.value = true
try {
// 清理旧的音频实例
if (audio.value) {
audio.value.removeEventListener('timeupdate', updateProgress)
audio.value.removeEventListener('ended', handleEnded)
}
// 检查歌曲URL是否存在
if (!currentSong.value.url) {
console.error('歌曲URL不存在:', currentSong.value)
audio.value = null
return false
}
// 创建新的音频实例
audio.value = new Audio(currentSong.value.url)
audio.value.volume = volume.value / 100
audio.value.playbackRate = speed.value
// 添加事件监听
audio.value.addEventListener('timeupdate', updateProgress)
audio.value.addEventListener('ended', handleEnded)
// 加载音频
await audio.value.load()
return true
} catch (error) {
console.error('加载音频失败:', error)
audio.value = null
return false
} finally {
isLoading.value = false
}
......@@ -182,18 +206,41 @@ const emit = defineEmits(['play', 'pause'])
// 播放控制:切换播放/暂停状态
const togglePlay = async () => {
// 如果正在加载中,不执行任何操作
if (isLoading.value) return
try {
// 如果音频实例不存在,尝试加载
if (!audio.value) {
await loadAudio()
const loadSuccess = await loadAudio()
// 如果加载失败,直接返回
if (!loadSuccess || !audio.value) {
console.warn('无法加载音频实例')
return
}
}
// 根据当前播放状态执行相应操作
if (isPlaying.value) {
await audio.value?.pause()
emit('pause', audio.value)
// 暂停播放
if (audio.value) {
await audio.value.pause()
emit('pause', audio.value)
}
} else {
await audio.value?.play()
emit('play', audio.value)
// 开始播放
if (audio.value) {
try {
await audio.value.play()
emit('play', audio.value)
} catch (playError) {
console.error('播放失败:', playError)
return
}
}
}
// 切换播放状态
isPlaying.value = !isPlaying.value
} catch (error) {
console.error('播放控制失败:', error)
......@@ -204,10 +251,14 @@ const togglePlay = async () => {
defineExpose({
togglePlay,
pause: () => {
// 只有在播放状态且音频实例存在时才执行暂停
if (isPlaying.value && audio.value) {
audio.value.pause();
isPlaying.value = false;
emit('pause', audio.value);
} else {
// 如果音频实例不存在或已经暂停,只更新状态
isPlaying.value = false;
}
},
isPlaying: () => isPlaying.value
......@@ -228,41 +279,81 @@ const handleEnded = () => {
// 控制方法:切换到上一首
const prevSong = async () => {
if (audio.value) {
try {
// 如果音频实例存在,先暂停并清除
if (audio.value) {
isPlaying.value = false
await audio.value.pause()
audio.value = null
}
// 计算上一首歌曲的索引
currentIndex.value = (currentIndex.value - 1 + props.songs.length) % props.songs.length
// 加载新的音频
const loadSuccess = await loadAudio()
// 确保音频实例加载成功后再播放
if (loadSuccess && audio.value) {
try {
await audio.value.play()
// 使用非线性映射来调整音量变化的灵敏度
const normalizedVolume = Math.pow(volume.value / 100, 2)
audio.value.volume = normalizedVolume
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
} catch (playError) {
console.error('播放上一首歌曲失败:', playError)
isPlaying.value = false
}
} else {
console.warn('无法加载上一首歌曲')
isPlaying.value = false
}
} catch (error) {
console.error('切换到上一首歌曲失败:', error)
isPlaying.value = false
await audio.value.pause()
audio.value = null
}
currentIndex.value = (currentIndex.value - 1 + props.songs.length) % props.songs.length
await loadAudio()
if (audio.value) {
await audio.value.play()
// 使用非线性映射来调整音量变化的灵敏度
const normalizedVolume = Math.pow(volume.value / 100, 2)
audio.value.volume = normalizedVolume
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
}
}
// 控制方法:切换到下一首
const nextSong = async () => {
if (audio.value) {
try {
// 如果音频实例存在,先暂停并清除
if (audio.value) {
isPlaying.value = false
await audio.value.pause()
audio.value = null
}
// 计算下一首歌曲的索引
currentIndex.value = (currentIndex.value + 1) % props.songs.length
// 加载新的音频
const loadSuccess = await loadAudio()
// 确保音频实例加载成功后再播放
if (loadSuccess && audio.value) {
try {
await audio.value.play()
// 使用非线性映射来调整音量变化的灵敏度
const normalizedVolume = Math.pow(volume.value / 100, 2)
audio.value.volume = normalizedVolume
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
} catch (playError) {
console.error('播放下一首歌曲失败:', playError)
isPlaying.value = false
}
} else {
console.warn('无法加载下一首歌曲')
isPlaying.value = false
}
} catch (error) {
console.error('切换到下一首歌曲失败:', error)
isPlaying.value = false
await audio.value.pause()
audio.value = null
}
currentIndex.value = (currentIndex.value + 1) % props.songs.length
await loadAudio()
if (audio.value) {
await audio.value.play()
// 使用非线性映射来调整音量变化的灵敏度
const normalizedVolume = Math.pow(volume.value / 100, 2)
audio.value.volume = normalizedVolume
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
}
}
......@@ -324,26 +415,46 @@ const closePlaylist = () => {
// 选择并播放指定歌曲
const selectSong = async (index) => {
if (audio.value) {
isPlaying.value = false
await audio.value.pause()
audio.value = null
}
currentIndex.value = index
await loadAudio()
if (audio.value) {
await audio.value.play()
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
// 关闭播放列表
isPlaylistVisible.value = false
// 滚动到当前播放的音频
const playlistItems = document.querySelector('.playlist-items')
const activeItem = playlistItems?.querySelector('.active')
if (playlistItems && activeItem) {
activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
try {
// 如果音频实例存在,先暂停并清除
if (audio.value) {
isPlaying.value = false
await audio.value.pause()
audio.value = null
}
// 设置当前索引
currentIndex.value = index
// 加载新的音频
const loadSuccess = await loadAudio()
// 确保音频实例加载成功后再播放
if (loadSuccess && audio.value) {
try {
await audio.value.play()
isPlaying.value = true
// 发射播放事件
emit('play', audio.value)
// 关闭播放列表
isPlaylistVisible.value = false
// 滚动到当前播放的音频
const playlistItems = document.querySelector('.playlist-items')
const activeItem = playlistItems?.querySelector('.active')
if (playlistItems && activeItem) {
activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
}
} catch (playError) {
console.error('播放选择的歌曲失败:', playError)
isPlaying.value = false
}
} else {
console.warn('无法加载选择的歌曲')
isPlaying.value = false
}
} catch (error) {
console.error('选择歌曲失败:', error)
isPlaying.value = false
}
}
......