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í)長(zhǎng) 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) { // 具體請(qǐng)求怎么寫就看每個(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) => { //事件對(duì)象兼容 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>
我為了方便測(cè)試,音頻路徑是寫在里面的,有用的同學(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音頻可視化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue3異步數(shù)據(jù)加載組件suspense的使用方法
前端開發(fā)中異步請(qǐng)求是非常常見的事情,比如遠(yuǎn)程讀取圖片,調(diào)用后端接口等等,這篇文章主要給大家介紹了關(guān)于Vue3異步數(shù)據(jù)加載組件suspense的使用方法,suspense中文含義是懸念的意思,需要的朋友可以參考下2021-08-08uniapp+vue3實(shí)現(xiàn)上傳圖片組件封裝功能
這篇文章主要介紹了uniapp+vue3實(shí)現(xiàn)上傳圖片組件封裝功能,首先創(chuàng)建一個(gè)?components 文件在里面進(jìn)行組件的創(chuàng)建,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07Vue?3?中?vue-router?的?router.resolve?()?API詳解
router.resolve()?就好比是一個(gè)精準(zhǔn)的?“導(dǎo)航參謀”,當(dāng)我們?cè)?Vue?3?應(yīng)用里需要明確某個(gè)路由地址對(duì)應(yīng)的詳細(xì)信息時(shí),它就能派上用場(chǎng),本文給大家介紹Vue?3?中?vue-router?的?router.resolve?()?API,感興趣的朋友一起看看吧2025-04-04vue history 模式打包部署在域名的二級(jí)目錄的配置指南
這篇文章主要介紹了vue history 模式打包部署在域名的二級(jí)目錄的配置指南 ,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07