音频波纹+文字气泡
<div class="box">
<div class="wave-bubble-content">
<div id="waveform" @click="getTime">
<canvas
id="wave-bubble-text"
class="wave-bubble-text"
></canvas>
</div>
</div>
<record-audio-player
v-if="audioUrl"
ref="audioPlayer"
class="audio-player"
:src="audioUrl"
name="音频名称"
@setCurrentTime="changeCurrentTime"
@setSpeed="onSetSpeed"
@ended="onEnded"
@pause="onPause"
@play="onPlay"
/>
</div>
methods: {
async getDetails() {
this.audioDetails = await this.$store.dispatch('audioAnalysis/one', {id: this.audioId});
const params = {
key: this.audioDetails.fileKey
};
const url = await this.getSoundUrl({params});
this.audioUrl = url;
const audioElement = new Audio(this.audioUrl);
audioElement.addEventListener('loadedmetadata', () => {
this.totalTime = audioElement.duration;
// 音频加载成功后
if (this.audioUrl) {
this.initWave();
const textCanvas = document.querySelector('#wave-bubble-text');
this.addMark(textCanvas);
}
});
},
// 设置文字气泡的位置
addMark(container) {
const canvas = document.createElement('canvas');
container.appendChild(canvas);
const canvasWidth = 3600;
// 画一个超大的canvas 否则字会很模糊
canvas.width = canvasWidth;
canvas.height = 360;
canvas.style.display = 'none';
const cansContext = canvas.getContext('2d');
cansContext.font = '30px Microsoft YaHei';
cansContext.textAlign = 'left';
this.resultList.map(item => {
// 文字最左边的的横坐标
const x = timeToSeconds(item.time) / this.totalTime * canvasWidth;
// 文字最上面的纵坐标
const y = 70;
// 文字像素大小 30px
const fSize = 30;
// 30px对应的字体高度 40px
const Yh = 40;
// 绘制气泡
cansContext.beginPath();
cansContext.moveTo(x, y);
cansContext.lineTo(x + item.text.length * fSize + 20, y);
cansContext.lineTo(x + item.text.length * fSize + 20, y + Yh);
cansContext.lineTo(x + 30, y + Yh);
cansContext.lineTo(x + 20, y + Yh + 15);
cansContext.lineTo(x + 10, y + Yh);
cansContext.lineTo(x, y + Yh);
cansContext.lineTo(x, y);
cansContext.closePath();
cansContext.stroke();
cansContext.fillStyle = '#2468f2';
cansContext.fill();
// 绘制文字
cansContext.fillStyle = 'white';
cansContext.fillText(item.text, x + 10, y + Yh - 10);
});
container.style.backgroundImage = 'url(' + canvas.toDataURL('image/png') + ')';
container.style.backgroundSize = '100%';
container.style.backgroundRepeat = 'no-repeat';
},
getSoundUrl(options) {
return customizeRequest.instance({
method: 'get',
url: options.url || apis.url.fileDownload,
params: {
...options.params,
getParent: false
},
headers: {
'Authorization': getAuthorization(options),
},
responseType: 'blob',
}).then(res => {
if (!res.headers['content-disposition']) {
return console.error('文件名称不存在');
}
// 获取到的blob类型文件地址
return URL.createObjectURL(res.data);
}).catch(err => {
console.error('文件获取失败');
});
},
initWave() {
// 官网文档地址 https://wavesurfer-js.org/docs/
this.wavesurfer = WaveSurfer.create({
// 波形图的容器
container: '#waveform',
// 已播放波形的颜色
progressColor: 'orange',
// 未播放波形的颜色
waveColor: 'green',
// 波形图的高度,单位为px
height: 120,
// 波形的振幅(高度),默认为1 ,总时长大于200s的稍微把波形振幅提高一点
barHeight: this.totalTime > 200 ? 0.6 : 0.4,
// 波形条的圆角
barRadius: 2,
// 波形条的宽度
barWidth: 1,
// 波形条间的间距
barGap: 3,
// 播放进度光标条的颜色
cursorColor: 'black',
// 播放进度光标条的宽度,默认为1
cursorWidth: 1,
// 波形容器的背景颜色
backgroundColor: 'white',
// 音频的播放速度
audioRate: '1',
// (与区域插件一起使用)启用所选区域的循环
loopSelection: false
});
// 这里需要后端支持cors 否则跨域 不过这种获取到的blob类型文件地址貌似就不跨域了
this.wavesurfer.load(this.audioUrl);
// 静音
this.wavesurfer.setMute(true);
},
// 点击获取点击进度
getTime() {
setTimeout(() => {
const currentTime = this.wavesurfer.getCurrentTime();
const audio = this.$refs.audioPlayer;
audio.setCurrentTime(currentTime);
// 点击波纹 会让audio进度条对应跳转,但会导致触发changeCurrentTime事件,因此用play方法覆盖,解决展示错误问题
// 让他看上去像是暂停一样,直接点波纹就是会播放,但是pm希望不播放,如果给pause,则光标展示的地方不对,目前只能暂时这么解决
this.wavesurfer.play(currentTime, currentTime + (1 / 100));
}, 100);
},
// 点击audio进度条事 设置wavesurfer对应的当前时间
changeCurrentTime() {
// 点击 audio 进度条的时候,让波纹同步的跳转到对应时间点
const currentTime = this.$refs.audioPlayer.currentTime;
const oldTime = this.wavesurfer.getCurrentTime();
this.wavesurfer.skip(currentTime - oldTime);
},
// 设置wavesurfer的播放速度
onSetSpeed(speed) {
this.wavesurfer.setPlaybackRate(speed);
},
onEnded() {
clearInterval(this.videoInterval);
},
// 暂停
onPause() {
clearInterval(this.videoInterval);
this.wavesurfer.pause();
},
// audio 按钮点击事件
onPlay() {
const currentTime = this.$refs.audioPlayer.$refs.audio.currentTime;
this.wavesurfer.play(currentTime);
let timer = parseInt(currentTime, 10);
this.videoInterval = setInterval(() => {
this.$refs.sessionBubble.moveToScreenView(timer);
timer++;
}, 1000);
},
handlePageChange(val) {
this.page = val;
this.getRecordDetail();
},
sizeChange(val) {
this.pageSize = val;
this.page = 1;
this.getRecordDetail();
}
}