Vue3音频组件开发与使用指南
前言
在现代Web应用中,音频播放功能越来越常见,特别是在娱乐和社交应用中。本文将详细介绍如何在Vue3项目中开发一个功能完整的音频播放组件,包括播放控制、进度条、时间显示以及多音频实例管理等功能。
效果
项目结构
复制代码
src/
├── components/
│ └── audio/
│ └── index.vue # 音频组件
├── store/
│ └── modules/
│ └── audio.ts # 音频状态管理
└── assets/
└── audio/
├── play.png # 播放按钮图标
└── pause.png # 暂停按钮图标
核心功能特性
✅ 播放/暂停控制
✅ 进度条拖拽调节
✅ 时间显示(当前时间/总时长)
✅ 播放速度调节
✅ 降噪功能
✅ 多音频实例管理
✅ 禁用状态支持
✅ 自定义样式
音频状态管理 (Pinia Store)
首先,我们使用Pinia来管理全局音频状态,确保同时只有一个音频在播放:
typescript
复制代码
// src/store/modules/audio.ts
import { defineStore } from 'pinia';
type AudioElement = HTMLAudioElement & { uid?: symbol };
export const useAudioStore = defineStore('audio', {
state: () => ({
instances: new Set
currentPlaying: null as AudioElement | null
}),
actions: {
// 注册音频实例
register(audio: AudioElement) {
if (!audio.uid) {
audio.uid = Symbol('audio-instance');
}
this.instances.add(audio);
},
// 注销音频实例
unregister(audio: AudioElement) {
this.instances.delete(audio);
},
// 暂停除当前音频外的所有音频
pauseAllExcept(currentAudio: AudioElement) {
this.instances.forEach(audio => {
if (audio.uid !== currentAudio.uid && !audio.paused) {
audio.pause();
audio.dispatchEvent(new Event('force-pause'));
}
});
this.currentPlaying = currentAudio;
}
}
});
状态管理设计思路
实例管理:使用Set存储所有音频实例,避免重复
唯一标识:为每个音频元素分配唯一的Symbol标识
互斥播放:确保同时只有一个音频在播放
事件通知:通过自定义事件通知组件状态变化
音频组件实现
组件Props定义
typescript
复制代码
const props = defineProps({
url: String, // 音频URL
speed: String, // 播放速度
noiseSuppression: Boolean, // 降噪开关
disabled: Boolean, // 禁用状态
isbg: String, // 背景样式
mock: { // 模拟模式
type: Boolean,
default: false,
},
playData: { // 播放数据
type: Object,
default: () => ({}),
},
res: { // 响应数据
type: String,
default: '',
},
});
核心响应式数据
typescript
复制代码
const audioPlayer = ref
const audioUrl = ref(props.url); // 音频URL
const progress = ref(0); // 播放进度
const timeCount = ref(0); // 总时长(秒*10)
const startTime = ref('00:00'); // 当前时间
const endTime = ref('00:00'); // 总时长
const isPlay = ref(false); // 播放状态
关键功能实现
1. 播放控制
typescript
复制代码
const playClick = () => {
if (endTime.value == '00:00') return;
if (!audioUrl.value || props.disabled || props.mock) return;
// 暂停其他音频
if (audioPlayer.value) {
audioStore.pauseAllExcept(audioPlayer.value);
}
startOrStop();
};
const startOrStop = () => {
isPlay.value = !isPlay.value;
emit('playClick', { ...props.playData, isPlay: isPlay.value, res: props.res });
if (isPlay.value) {
audioPlayer.value?.play();
// 启动进度更新定时器
time.value = setInterval(() => {
progress.value++;
startTime.value = secondToTime(progress.value / 10);
}, 100);
} else {
audioPlayer.value?.pause();
clearInterval(time.value);
clearInterval(time1.value);
}
};
2. 进度条控制
typescript
复制代码
// 进度条拖拽中
const proChange = () => {
if (isDown.value) {
clearInterval(time.value);
clearInterval(time1.value);
audioPlayer.value.pause(progress.value / 10);
}
};
// 进度条拖拽结束
const proUp = () => {
isDown.value = false;
audioPlayer.value.currentTime = progress.value / 10;
audioPlayer.value.play();
clearInterval(time.value);
clearInterval(time1.value);
isPlay.value = true;
// 重新启动进度更新
time1.value = setInterval(() => {
progress.value++;
startTime.value = secondToTime(progress.value / 10);
}, 100);
};
3. 时间格式化
typescript
复制代码
const secondToTime = (val: number) => {
if (!isFinite(val)) return '0:00';
val = Math.round(val);
const minutes = Math.floor(val / 60);
const seconds = val % 60;
const secondsStr = seconds < 10 ? `0${seconds}` : seconds;
return `${minutes}:${secondsStr}`;
};
4. 生命周期管理
typescript
复制代码
onMounted(() => {
if (audioPlayer.value) {
// 注册到全局store
audioStore.register(audioPlayer.value);
// 监听外部暂停事件
audioPlayer.value.addEventListener('force-pause', () => {
isPlay.value = false;
clearInterval(time.value);
clearInterval(time1.value);
});
// 初始化音频时长
if (audioUrl.value) {
playAudio();
}
}
});
onUnmounted(() => {
if (audioPlayer.value) {
audioStore.unregister(audioPlayer.value);
}
});
组件模板结构
vue
复制代码
v-model="progress" :show-tooltip="false" :max="timeCount" :disabled="disabled || mock" class="jindu" @input="proChange" @change="proUp" /> {{ startTime }}/ {{ endTime }}
使用方法
1. 基础使用
vue
复制代码
import Audio from '@/components/audio/index.vue';
const audioUrl = ref('https://example.com/audio.mp3');
2. 完整配置
vue
复制代码