背景
最近在维护一个 Vue2 的后台管理系统,需要在 PC 端播放移动端上传的录音文件MP3,一开始我尝试使用原生的 标签来实现,但在拖动进度条时,音频总时长会异常变化,播放体验很差。
于是就想看看有没有更专业的库来处理波形和播放控制,最后发现了 wavesurfer.js
一个专门做音频可视化和播放控制的轻量库,试了一下发现效果很不错,功能简洁,扩展性强。
需求
- 在后台页面播放移动端录音
- 可视化波形,支持点击进度跳转
- 显示 播放进度/总时长
- 支持 播放速度调节
- 支持 播放 / 暂停 / 停止
实现效果

wavesurfer 安装与引入
javascript
npm install wavesurfer
# 或者
yarn add wavesurfer
# 在 Vue2 组件中引入:
import WaveSurfer from "wavesurfer";
完整代码
javascript
<template>
<div class="app-container">
<div
class="audio-container"
style="display: flex; align-items: center; gap: 12px; width: 100%;"
v-show="showAudio"
>
<!-- 波形图 -->
<div ref="waveform" style="flex: 1; height: 80px;"></div>
<!-- 播放时间 -->
<span style="white-space: nowrap;">
{{ formatTime(currentTime) }} / {{ formatTime(duration) }}
</span>
<!-- 播放速度 -->
<div style="display: flex; align-items: center; gap: 4px;">
<label>播放速度:</label>
<el-select v-model="speed" @change="changeSpeed" style="width: 70px;">
<el-option v-for="s in speeds" :key="s" :value="s"
>{{ s }}x</el-option
>
</el-select>
</div>
<!-- 播放/暂停/关闭按钮 -->
<div style="display: flex; align-items: center; gap: 8px;">
<el-button type="primary" @click="playAudio">播放</el-button>
<el-button type="warning" @click="pauseAudio">暂停</el-button>
<el-button type="default" @click="stopAudio">关闭</el-button>
</div>
</div>
</div>
</template>
<script>
import WaveSurfer from "wavesurfer";
export default {
name: "soundRecording",
data() {
return {
wavesurfer: null,
showAudio: false,
palyAudioUrl: "",
currentTime: 0,
duration: 0,
speed: 1,
speeds: [0.5, 0.75, 1, 1.25, 1.5, 2]
};
},
methods: {
// 格式化时间
formatTime(seconds) {
const m = Math.floor(seconds / 60)
.toString()
.padStart(2, "0");
const s = Math.floor(seconds % 60)
.toString()
.padStart(2, "0");
return `${m}:${s}`;
},
// 初始化 WaveSurfer
initWaveform(url) {
// 销毁已有实例
if (this.wavesurfer) {
this.wavesurfer.destroy();
this.wavesurfer = null;
}
// 初始化 WaveSurfer
this.wavesurfer = WaveSurfer.create({
container: this.$refs.waveform,
waveColor: "#A8DBA8",
progressColor: "#3B8686",
height: 80,
responsive: true
});
// 监听加载完成
this.wavesurfer.on("ready", () => {
this.duration = this.wavesurfer.getDuration();
this.wavesurfer.setPlaybackRate(this.speed);
// 🔹 音频加载完成后再播放
this.wavesurfer.play();
});
// 更新播放时间
this.wavesurfer.on("audioprocess", () => {
this.currentTime = this.wavesurfer.getCurrentTime();
});
// 播放结束
this.wavesurfer.on("finish", () => {
this.currentTime = 0;
});
// 监听用户点击波形图进度
this.wavesurfer.on("seek", progress => {
// progress 是 0~1 的比例,转换成秒
this.currentTime = progress * this.wavesurfer.getDuration();
});
// 加载音频
this.wavesurfer.load(url);
this.playAudio();
},
// 初始化播放录音,传入录音数据
playRecording(row) {
this.showAudio = true;
let url = row.blob ? URL.createObjectURL(row.blob) : row.url;
this.audioUrl = url;
this.initWaveform(url);
},
// 播放音频
playAudio() {
if (this.wavesurfer) this.wavesurfer.play();
},
// 暂停音频
pauseAudio() {
if (this.wavesurfer) this.wavesurfer.pause();
},
// 关闭音频
stopAudio() {
if (this.wavesurfer) {
this.wavesurfer.stop();
this.currentTime = 0;
}
this.showAudio = false;
},
// 改变播放速度
changeSpeed() {
if (this.wavesurfer) this.wavesurfer.setPlaybackRate(this.speed);
}
},
mounted() {
//初始化音频链接,可以通过点击获取音频链接再进行赋值,只有一个音频的话,也可以这样初始化
this.playRecording({
url:'https://xxx.com/example.mp3',//更换真实的mp3音频链接
});
}
};
</script>