Vue使用wavesurfer.js實(shí)現(xiàn)音頻可視化的示例詳解
WaveSurfer.js是一個(gè)開源的音頻可視化庫,用于創(chuàng)建交互式、可定制的波形。文章詳細(xì)介紹了配置選項(xiàng),包括播放速度、音頻渲染、波形樣式、交互性等,并提供了事件處理方法如播放、暫停、音量控制等。本文要實(shí)現(xiàn)的效果圖如下:

安裝
按照官方文檔步驟
1.安裝
npm install --save wavesurfer.js
2.引入
import WaveSurfer from 'wavesurfer.js' //需要時(shí)間軸的話需要引入 import Timeline from 'wavesurfer.js/dist/plugins/timeline.esm.js'
封裝Vue 組件
接下來,我們將 WaveSurfer.js 封裝成一個(gè) Vue 組件,方便在項(xiàng)目中復(fù)用。以下是完整的組件代碼
<template>
<div id="aiccAudio">
<el-card>
<div id="wavefrom" ref="wavefrom" @click="getTime">
</div>
</el-card>
<div class="audio-controlBar">
<!-- 播放/暫停按鈕 -->
<button @click="playMusic">
<i v-if="videoPlay" class="el-icon-video-play"></i>
<i v-else class="el-icon-video-pause"></i>
</button>
<!-- 時(shí)間 -->
<div class="audio-time">
<div class="audio-current-time" aria-label="time">{{ currentTime }}</div>
<span class="audio-fen-line">|</span>
<div class="audio-duration" aria-label="duration">{{ duration }}</div>
</div>
<!-- 倍速 -->
<span style="margin-left: 80px;margin-right: 10px;">倍速</span>
<el-select class="audio-speed" v-model="speedValue" @change="speedChange">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
<!-- <span class="audio-line">|</span> -->
<!-- 音量 -->
<div class="audio-volume">
<img v-if="audioVolumeStatus == true" @click="volumeStatusChange('open')" class="audio-volume-img"
src="../../../../public/images/voice_open.png">
<img v-else class="audio-volume-img" @click="volumeStatusChange('close')"
src="../../../../public/images/voice_close.png">
<el-slider class="audio-volume-slider" v-model="volume" @input="volumeChange"></el-slider>
</div>
</div>
</div>
</template>
<script>
import WaveSurfer from 'wavesurfer.js' //導(dǎo)入wavesurfer.js
import Timeline from 'wavesurfer.js/dist/plugins/timeline.esm.js' //導(dǎo)入時(shí)間軸插件
export default {
name: 'AiccAudio',
props: {
src: {
type: String
},
recordPath: {
type: String
}
},
data() {
return {
wavesurfer: null,
timer: null,
options: [
{
value: '0.5',
label: '0.5',
}, {
value: '1.0',
label: '1.0',
}, {
value: '1.5',
label: '1.5',
}, {
value: '2.0',
label: '2.0',
}
],
speedValue: '1.0',
currentTime: '00:00:00', //當(dāng)前播放
duration: '00:00:00', //總時(shí)長
volume: 30,//音量,
audioVolumeStatus: false, //是否靜音
videoPlay: true,
}
},
created() {
this.keyDown();
},
mounted() {
this.$nextTick(() => {
if (this.recordPath != null && this.recordPath != '') {
this.wavesurfer = WaveSurfer.create({
container: this.$refs.wavefrom,
height: 128,
waveColor: 'rgb(200, 0, 200)',
progressColor: '#00f2fe',
cursorColor: '#00f2fe', //指示播放頭位置的光標(biāo)填充顏色
cursorWidth: 2,
// backend: 'MediaElement',
mediaControls: false,
audioRate: this.speedValue,
hideScrollbar: true,
setPlaybackRate: [0.5, 1.0, 1.5, 2.0],
//使用時(shí)間軸插件
plugins: [Timeline.create()],
})
this.wavesurfer.load('http://localhost:9527/89A3099B9253566.mp3')
console.log(this.wavesurfer)
//音頻加載完成觸發(fā)
this.wavesurfer.on("ready", () => {
this.wavesurfer.setVolume(this.volume / 100);
this.duration = this.formatSecond(this.wavesurfer.getDuration())
});
this.wavesurfer.on('click', () => {
this.wavesurfer.play()
this.videoPlay = false
})
}
})
},
methods: {
getSoundUrl(options) {
// 具體請求怎么寫就看每個(gè)人的項(xiàng)目用的是什么了
return axios.get(url, { responseType: 'blob' })
.then(res => {
if (!res.headers['content-disposition']) {
return console.error('文件名稱不存在');
}
// 獲取到的blob類型文件地址
return URL.createObjectURL(res.data);
})
.catch(err => {
console.error('文件獲取失敗');
})
},
playMusic() {
if (this.recordPath != null && this.recordPath != '') {
//"播放/暫停"按鈕的單擊觸發(fā)事件,暫停的話單擊則播放,正在播放的話單擊則暫停播放
this.wavesurfer.playPause.bind(this.wavesurfer)();
//每秒獲取進(jìn)度 有時(shí)間軸時(shí)使用
// this.getProcess();
//判斷是否播放
this.videoPlay = this.wavesurfer.isPlaying() == true ? false : true;
//音頻播放時(shí)觸發(fā)
this.wavesurfer.on("audioprocess", () => {
this.currentTime = this.formatSecond(this.wavesurfer.getCurrentTime())
})
//結(jié)束播放
this.wavesurfer.on("finish", () => {
this.wavesurfer.stop();
this.videoPlay = true;
this.currentTime = '00:00:00'
});
}
},
formatSecond(seconds) {
// console.log(seconds)
// 確保輸入是數(shù)字類型
if (typeof seconds !== 'number' || isNaN(seconds)) {
this.$message.error('error')
}
// 將秒數(shù)拆分為小時(shí)、分鐘和秒
const totalSeconds = Math.floor(seconds); // 只保留整數(shù)部分
const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0'); // 計(jì)算小時(shí)并補(bǔ)零
const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0'); // 計(jì)算分鐘并補(bǔ)零
const secs = String(totalSeconds % 60).padStart(2, '0'); // 計(jì)算秒并補(bǔ)零
return `${hours}:${minutes}:${secs}`;
},
//有時(shí)間軸時(shí)使用
// getProcess(){
// if(this.wavesurfer.isPlaying()){
// this.timer=setInterval(()=>{
// this.percent=(this.wavesurfer.getCurrentTime().toFixed(0)/this.wavesurfer.getDuration()*100).toFixed(0)
// this.config={
// value:this.percent
// }
// },1000)
// }else{
// clearInterval(this.timer)
// }
// },
//點(diǎn)擊獲取點(diǎn)擊進(jìn)度
getTime() {
if (this.recordPath != null && this.recordPath != '') {
//加定時(shí)器,不然獲取的是點(diǎn)擊前的播放時(shí)間
setTimeout(() => {
this.currentTime = this.formatSecond(this.wavesurfer.getCurrentTime())
///有時(shí)間軸時(shí)使用
// this.percent=(this.wavesurfer.getCurrentTime()/this.wavesurfer.getDuration()*100).toFixed(0)
// this.config={
// value:this.percent
// }
}, 100)
}
},
//按鍵跳轉(zhuǎn)進(jìn)度
// jump(e){
// this.wavesurfer.play([e.target.innerHTML-0])
// this.percent=(this.wavesurfer.getCurrentTime().toFixed(0)/this.wavesurfer.getDuration()*100).toFixed(0)
// this.config={
// value:this.percent
// }
// this.getProcess()
// },
// 監(jiān)聽鍵盤
keyDown() {
if (this.recordPath != null && this.recordPath != '') {
document.onkeydown = (e) => {
//事件對象兼容
var eCode = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;
//鍵盤按鍵判斷:左箭頭-37;上箭頭-38;右箭頭-39;下箭頭-40
//左
if (eCode == 37) {
// 按下左箭頭
this.wavesurfer.skip(-6)
this.getTime()
} else if (eCode == 39) {
// 按下右箭頭
this.wavesurfer.skip(6)
this.getTime()
}
}
}
},
//倍速
speedChange(val) {
if (this.recordPath != null && this.recordPath != '') {
this.speedValue = val;
this.wavesurfer.setPlaybackRate(this.speedValue)
}
},
//音量大小改變
volumeChange(val) {
if (this.recordPath != null && this.recordPath != '') {
this.volume = val;
if (this.wavesurfer != null) {
this.wavesurfer.setVolume(val / 100);
}
if (val == '0') {
this.audioVolumeStatus = false
} else {
this.audioVolumeStatus = true
}
}
},
//靜音開啟關(guān)閉
volumeStatusChange(status) {
if (this.recordPath != null && this.recordPath != '') {
if (status == 'open') {
this.audioVolumeStatus = false
this.volume = 0;
this.wavesurfer.setVolume(0);
} else {
this.audioVolumeStatus = true
this.volume = 30;
this.wavesurfer.setVolume(30 / 100);
}
}
}
}
}
</script>
<style lang="scss" scoped>
.audio-controlBar {
background-color: #f4f6f9;
padding: 0 10px;
border-radius: 10px;
button {
border: none;
background-color: #f4f6f9;
}
i {
//color: #95979f;
color: #1053ee;
}
}
//時(shí)間
.audio-time {
display: inline-block;
font-size: 12px;
color: #95979f;
margin: 0 10px;
}
.audio-current-time {
display: inline-block;
color: #1053ee;
}
.audio-fen-line {
margin: 0 5px;
}
.audio-duration {
display: inline-block;
}
//倍速
.audio-speed {
width: 68px;
// margin-left: 85px;
.el-input__inner {
background-color: #f4f6f9;
}
}
.audio-line {
color: #95979f;
margin-right: 5px;
}
//音量條
.audio-volume {
width: 166px;
float: right;
.audio-volume-slider {
width: 110px;
float: right;
}
}
.audio-volume-img {
width: 18px;
margin-top: 12px;
}
</style>我為了方便測試,音頻路徑是寫在里面的,有用的同學(xué)可以根據(jù)項(xiàng)目需求傳入組件中,樣式功能啥的可以自行調(diào)整。
總結(jié)
通過 WaveSurfer.js,我們可以輕松實(shí)現(xiàn)音頻的可視化與播放控制。本文介紹的 Vue 組件封裝了常見的音頻播放功能,包括播放/暫停、倍速控制、音量調(diào)節(jié)等,方便在項(xiàng)目中復(fù)用。根據(jù)項(xiàng)目需求,可以進(jìn)一步調(diào)整樣式和功能。
以上就是Vue使用wavesurfer.js實(shí)現(xiàn)音頻可視化的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue wavesurfer.js音頻可視化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決vue3傳屬性時(shí)報(bào)錯(cuò)[Vue?warn]:Component?is?missing?template?or
這篇文章主要給大家介紹了關(guān)于解決vue3傳屬性時(shí)報(bào)錯(cuò)[Vue?warn]:Component?is?missing?template?or?render?function的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
Vue中Axios配置不同的baseURL,請求不同的域名接口方式
這篇文章主要介紹了Vue中Axios配置不同的baseURL,請求不同的域名接口方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
vue在頁面和方法中如何通過遍歷對象獲取對象的鍵(key)和值(value)
這篇文章主要介紹了vue在頁面和方法中如何通過遍歷對象獲取對象的鍵(key)和值(value)問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
vue select二級聯(lián)動第二級默認(rèn)選中第一個(gè)option值的實(shí)例
下面小編就為大家分享一篇vue select二級聯(lián)動第二級默認(rèn)選中第一個(gè)option值的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01

