AudioPlayer.vue
<template>
<!-- 这个组件是一个逻辑组件,不需要渲染任何UI -->
<span></span>
</template>
<script lang="ts">
import { defineComponent, watch, onUnmounted, ref } from 'vue'
export default defineComponent({
name: 'AudioPlayer',
props: {
// 音频源,可以是URL或base64数据
src: {
type: String,
required: true
},
// 控制播放状态:'play' 开始播放,'stop' 停止播放
command: {
type: String as () => 'play' | 'stop',
default: 'stop',
validator: (value: string) => ['play', 'stop'].includes(value)
},
// 是否循环播放
loop: {
type: Boolean,
default: false
},
// 音量 0-1
volume: {
type: Number,
default: 1,
validator: (value: number) => value >= 0 && value <= 1
}
},
emits: ['play-start', 'play-end', 'play-error'],
setup(props, { emit }) {
const audioRef = ref<HTMLAudioElement | null>(null)
let audio: HTMLAudioElement | null = null
// 初始化音频
const initAudio = () => {
if (audio) {
destroyAudio()
}
audio = new Audio(props.src)
audio.loop = props.loop
audio.volume = props.volume
audio.addEventListener('ended', handlePlayEnd)
audio.addEventListener('error', handlePlayError)
}
// 销毁音频
const destroyAudio = () => {
if (audio) {
stop()
audio.removeEventListener('ended', handlePlayEnd)
audio.removeEventListener('error', handlePlayError)
audio = null
}
}
// 播放音频
const play = async () => {
if (!audio) {
initAudio()
}
try {
if (audio) {
await audio.play()
emit('play-start')
}
} catch (error) {
emit('play-error', error)
}
}
// 停止播放
const stop = () => {
if (audio && !audio.paused) {
audio.pause()
audio.currentTime = 0
emit('play-end')
}
}
const handlePlayEnd = () => {
if (!props.loop) {
emit('play-end')
}
}
const handlePlayError = (error: Event) => {
emit('play-error', error)
}
// 监听命令变化
watch(() => props.command, (newCommand) => {
if (newCommand === 'play') {
play()
} else if (newCommand === 'stop') {
stop()
}
})
// 监听src变化
watch(() => props.src, () => {
initAudio()
})
// 监听音量变化
watch(() => props.volume, (newVolume) => {
if (audio) {
audio.volume = newVolume
}
})
// 监听循环设置变化
watch(() => props.loop, (newLoop) => {
if (audio) {
audio.loop = newLoop
}
})
// 组件卸载时清理
onUnmounted(() => {
destroyAudio()
})
return {
audioRef
}
}
})
</script>
使用AudioPlayer.vue
(该组件有个bug时好时坏,详情描述:需播放4个音频文件,点击播放按钮播放完第一个音频后,在音频播放完成方法onPlayEnd中继续写播放第二个音频,其次第三个,依此类推,会出现只播放完第一个音频后其余的几个都不播放,或者播放完前3个第四个不播放的情况,但在控制台打印都正常,所以有了第二个音频组件。q_q)
<template>
<button class='btn' @click='playAudio'>点击播放音频</button>
<AudioPlayer
:src="audioSrc"
:command="playCommand"
@play-start="onPlayStart"
@play-end="onPlayEnd"
@play-error="onPlayError"
v-if="audioSrc"
/>
</temp
late>
<script lang="ts" steup>
import { ref } from 'vue'
import AudioPlayer from '@/components/AudioPlayer.vue'
import audio1 from '@/assets/audio/1.MP3'
const audioSrc = ref<any>(audio1);
const playCommand = ref<'play' | 'stop'>('stop')
// 开始播放音频
const startPlay = (src : any) => {
stopPlay();
audioSrc.value = src;
setTimeout(() => {
playCommand.value = 'play'
}, 100);
}
// 停止播放音频
const stopPlay = () => {
console.log('停止播放音频')
playCommand.value = 'stop'
}
// 音频播放开始
const onPlayStart = () => {
console.log('音频开始播放')
}
// 音频播放结束
const onPlayEnd = () => {
// console.log('音频播放结束')
playCommand.value = 'stop'; // 重置状态
}
// 音频播放出错
const onPlayError = (error: Event) => {
console.error('播放错误:', error)
playCommand.value = 'stop' // 出错时也重置状态
}
AudioPlayerV2.vue (解决上一个组件连续播放多个音频无声bug)
<template>
<audio controls ref="audioPlayer"></audio>
</template>
<script lang="ts" setup>
import { ref, defineEmits, defineProps, defineExpose } from 'vue'
//组件参数获取
const props = defineProps({
// 是否显示弹框
audioList: {
type: Array,
default: [] as any[]
// 当前播放的音频索引
},
});
const audioPlayer = ref<HTMLAudioElement | null>(null);
let currentIndex = 0;
const playAudioSequence = () => {
currentIndex = 0
playNextAudio()
}
const playNextAudio = () => {
if (!audioPlayer.value || currentIndex >= props.audioList.length) return
// 设置音频源
audioPlayer.value.src = String(props.audioList[currentIndex]);
// 播放当前音频
audioPlayer.value.play().catch(error => {
console.error('播放失败:', error)
})
// 监听播放结束事件
audioPlayer.value.onended = () => {
currentIndex++
if (currentIndex < props.audioList.length) {
// 延迟500毫秒播放下一个音频
setTimeout(playNextAudio, 500)
}
}
}
defineExpose({
playAudioSequence,
})
</script>
使用方法
<template>
<button class='btn' @click='playAudioSequence'>点击播放音频</button>
<AudioPlayerV2 ref="audioPlayer" :audio-list="audioList"/>
</temp
late>
<script lang="ts" steup>
import { ref } from 'vue'
import AudioPlayerV2 from '@/components/m/AudioPlayerV2.vue';
import audio1 from '@/assets/audio/1.MP3'
import audio2 from '@/assets/audio/2.MP3'
import audio3 from '@/assets/audio/3.MP3'
import audio4 from '@/assets/audio/4.MP3'
const audioPlayer = ref<InstanceType<typeof AudioPlayerV2> | null>(null);
const audioList = ref([audio1, audio2, audio3, audio4]);
const playAudioSequence = () => {
audioPlayer.value?.playAudioSequence();
}