在Vue中使用音频可视化插件wavesurfer.js

10,638 阅读1分钟

链接自取

一、基本用法

1.下载wavesurfer.js

$ npm install wavesurfer.js --save
# or 
$ yarn add wavesurfer.js

2.导入

在模块中导入

<script>
  import WaveSurfer from 'wavesurfer.js'
  import Timeline from 'wavesurfer.js/dist/plugin/wavesurfer.timeline'//Timeline插件
  import Region from 'wavesurfer.js/dist/plugin/wavesurfer.regions'//regions插件
  ...
  export default {}
</script>

3.创建一个容器

<template>
  <div>
      <!-- 时间线容器 -->
      <div id="timeline" ref="timeline" />
      <!-- 音频容器 -->
      <div id="waveform" ref="waveform" />
  </div>
</template>

4.创建一个实例

data(){
    return{
        wavesurfer: '',
     	speed: 1	
    }
},
methods:{
...
this.wavesurfer = WaveSurfer.create({
    container: this.$refs.waveform,//绑定容器,第一种方法
    // container: document.querySelector('#waveform'),//第二种方法
    // container: '#waveform',//第三种方法
    audioRate: this.speed,//控制播放速度
    forceDecode: true,   
    waveColor: '#A8DBA8',
    progressColor: '#3B8686',
    backend: 'MediaElement'
})
this.wavesurfer.load('https://mindflowai-open.oss-cn-hangzhou.aliyuncs.com/505/6mariyokwzjuqjql.wav')//加载音频
...
}

二、Timeline插件

1.导入插件

...
import Timeline from 'wavesurfer.js/dist/plugin/wavesurfer.timeline'
...

2.简单时间线实例

...
this.wavesurfer = WaveSurfer.create({
    container: this.$refs.waveform,
    ...
    plugins: [
        Timeline.create({
            container: this.$refs.timeline,,//绑定容器
            labelPadding: 2
        })
     ]
})
...

更多参数

3. 自定义复杂时间线

...
Timeline.create({
    container: '#timeline',
    secondaryColor: '#FF0000',//次要时间标签颜色,红色
    secondaryFontColor: '#FF0000',
    secondaryLabelInterval: this._secondaryLabelInterval,
    primaryColor: '#3498DB',//主要时间标签颜色,蓝色
    primaryFontColor: '#D3498DB',
    primaryLabelInterval: this._primaryLabelInterval,
    formatTimeCallback: this._formatTimeCallback,
    timeInterval: this._timeInterval,
    labelPadding: 2
})
...

4.重写formatTimeCallback方法

重写时间线的时间格式:

_formatTimeCallback(seconds, pxPerSec) {
    seconds = Number(seconds)
    var minutes = Math.floor(seconds / 60)
    seconds = seconds % 60
    var secondsStr = Math.round(seconds).toString()
    if (pxPerSec >= 25 * 10) {
        secondsStr = seconds.toFixed(2)
    } else if (pxPerSec >= 25 * 1) {
        secondsStr = seconds.toFixed(1)
    }
    if (minutes > 0) {
    	if (seconds < 10) {
            secondsStr = '0' + secondsStr
        }
     	return `${minutes}:${secondsStr}`
    }
    return secondsStr
}

5.重写timeInterval方法

重写时间间隔数,以分钟为单位的持续时间:

/**
* @param pxPerSec
* @return 以分钟为单位的值
*/
_timeInterval(pxPerSec) {
    var retval = 1
    if (pxPerSec >= 100) { // 0.5,1,1.5,2,...,9.5,10
        retval = 0.5
    } else if (pxPerSec >= 80) { // 1,2,...,9,10
        retval = 1
    } else if (pxPerSec >= 60) { // 2,4,6,8,10
        retval = 2
    } else if (pxPerSec >= 40) { // 5,10
        retval = 1
    } else if (pxPerSec >= 20) {
        retval = 5
    } else {
        retval = Math.ceil(0.5 / pxPerSec) * 60
    }
    return retval
},

6.重写primaryLabelInterval方法

重写主要时间标签的数量:

_primaryLabelInterval(pxPerSec) {
    var retval = 1
    if (pxPerSec >= 100) {
        retval = 2
    } else if (pxPerSec >= 80) {
        retval = 1
    } else if (pxPerSec >= 60) {
        retval = 1
    } else if (pxPerSec >= 40) {
        retval = 5
    } else if (pxPerSec >= 20) {
        retval = 2
    } else {
        retval = 1
    }
    return retval
},

7.重写secondaryLabelInterval方法

重写次要时间标签的数量:

_secondaryLabelInterval(pxPerSec) {
    if (pxPerSec >= 20 && pxPerSec < 40) {
        return 12
    } else if (pxPerSec >= 0 && pxPerSec < 20) {
        return 10
    } else {
        return Math.floor(10 / this._timeInterval(pxPerSec))
    }
},

8. timeIntervalprimaryLabelIntervalsecondaryLabelInterval关系

以传入的pxPerSec大于等于100为例,_timeInterval(100)返回0.5,_primaryLabelInterval(100)返回2。此时,主要标签的时间间隔是1秒(0.5*2),由2个0.5组成。_secondaryLabelInterval(100)返回10个1秒


三、方法

1.播放

//play()
this.wavesurfer.play()

2.暂停

//pause()
this.wavesurfer.pause()

3.回到开始并停止

 //stop()
 this.wavesurfer.stop()

4.重播

 //play([start[,end]]),可选参数
this.wavesurfer.play(0)

5.缩放

//zoom(pxPerSec)
this.wavesurfer.zoom(Number(this.valueZoom))

6.播放速度

//setPlaybackRate(rate)
this.wavesurfer.setPlaybackRate(this.speed)

7.调节声音

//setVolume(newVolume)-将播放音量设置为新值[0..1](0 =静音,1 =最大)
this.wavesurfer.setVolume(Number(this.valueRound * 0.01))

8.格式化时间

changeTime(seconds) {
    seconds = Number(seconds)
    var minutes = Math.floor(seconds / 60)
    seconds = seconds % 60
    var secondsStr = Math.round(seconds).toString()
    secondsStr = seconds.toFixed(2)
    if (minutes > 0) {
        return `${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + secondsStr : secondsStr}`
    }
    return `00:${seconds < 10 ? '0' + secondsStr : secondsStr}`
},

更多方法


四、事件

1.区域插件的region-click事件

点击区域,打印该区域开始和结束的时间:

this.wavesurfer.on('region-click', (e)=> {
    const { start, end } = e
    console.log(this.changeTime(start), this.changeTime(end))
})

2.获取当前播放时间

监听audioprocess事件,返回当前时间:

this.wavesurfer.on('audioprocess', function(e) {
    this.currentTime =this.changeTime(this.wavesurfer.getCurrentTime())
})

五、Regions插件

1.导入

import Region from 'wavesurfer.js/dist/plugin/wavesurfer.regions'

2.初始化

...
this.wavesurfer = WaveSurfer.create({
    container: this.$refs.waveform,
    ...
    plugins: [
        Timeline.create({...}),
        Region.create({
              regions: [
                {
                  start: 1,		//开始时间
                  end: 3,		//结束时间
                  loop: false,	//是否循环播放
                  color: 'hsla(400, 100%, 30%, 0.5)'//区域颜色
                }, {
                  start: 5,
                  end: 7,
                  loop: false,
                  color: 'hsla(200, 50%, 70%, 0.4)'
                }
              ],
              dragSelection: {
                slop: 5
              }
        }),
    ]
})
...

3.需求

  • 新增区域后播放自动播放当前区域音频,并同时新增一条表格信息(包含开始时间、结束时间等);
  • 点击区域自动播放当前区域音频,并高亮对应的表格信息;
  • 删除表格信息的同时,删除在音频中对应的区域;

4.运用

1.获取区域列表

getRegionList(listArr) {
    const _this = this
    _this.tableData = []// 清除
    if (listArr.length !== 0) {
        for (let i = listArr.length - 1; i >= 0; i--) { // 最新的在最前面
            _this.tableData.push({ id: listArr[i].id, startTime: _this.changeTime(listArr[i].start), endTime: _this.changeTime(listArr[i].end) })
        }
        _this.total = listArr.length
    }
}

2.点击区域,播放当前音频

this.wavesurfer.on('region-click', (region, mouseEvent)=> {
    this.currentRegion = region
    region.play() // 播放当前区域,另一种播放方式: this.wavesurfer.play(start, end)
})

3.点击区域,对应的列表项高亮

el-table标签添加row-class-name属性:

<el-table 
    :data="tableData" 
    :row-class-name="tableRowClassName"
>
...
            

tableRowClassName方法如下:

tableRowClassName({ row, rowIndex }) {
    if (this.currentRegion.id === row.id) {//通过区域id来判断
        return 'success-row'
    }
    return ''
}

给表格添加样式:

.el-table .success-row {
    background: #d9e2f8;
}

4.初次获取区域列表,并生成对应的表格

//wavesurfer.regions.list:获取音频中的区域列表
this.regionList = Object.values(this.wavesurfer.regions.list)
//将处理后的数据传入表格
this.getRegionList(this.regionList)

5.新增区域,播放当前音频,新增表格项

this.wavesurfer.on('region-update-end', (region) => {
    /** 播放区域的两种方式 */
    // this.wavesurfer.play(this.currentRegion.start, this.currentRegion.end)//①
    region.play()// ②
    
    /** 新增区域列表 */
    this.regionList = Object.values(this.wavesurfer.regions.list)
    this.getRegionList(this.regionList)
})

6.删除表格项,删除区域

deleteRegion(row) {
    const arr = this.regionList
    if (arr) {
        arr.forEach((region, index) => {
            if (row.id === region.id) {
                region.remove()
            	arr.splice(index, 1)
            	this.getRegionList(arr)
            }
        })
    } 
},

六、Cursor插件

1.导入

import Cursor from 'wavesurfer.js/dist/plugin/wavesurfer.cursor'

2.初始化

...
this.wavesurfer = WaveSurfer.create({
    container: this.$refs.waveform,
    ...
    plugins: [
        Timeline.create({...}),
        Region.create({...}),
        Cursor.create({
            showTime: true,
            opacity: 1,
            customShowTimeStyle: {
                'background-color': '#000',
                 color: '#fff',
                 padding: '2px',
                'font-size': '10px'
            }
        })
    ]
})
...

3.完善样式

到目前为止的光标是全屏显示的,理想的样子是光标只在音频区域显示,需要添加样式如下:

#waveform{
    position: relative;
}