Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互 (2)

参考这个实现,我们可以在每次监听到有数据写入时,从buffer中获取到录制到的数据,并进行编码、压缩,再通过WebSocket发送。

Vue组件设计和业务实现

分析页面业务逻辑,将代码拆分成两个组件:
ChatDialog.vue 聊天对话框页面,根据输入类型,分为文本输入、语音输入。
ChatRecord.vue聊天记录组件,根据发送方(自己或者系统)展示向左/向右的气泡,根据内容显示文本、音频等。ChatDialog是ChatRecord的父组件,遍历ChatDialog中的chatList对象(Array),将chatList中的项注入到ChatRecord中。

<div> <div v-for="(item,index) in chatList" :key="index"> <chat-record ref="chatRecord" :data="item" @showJson="showJsonDialog"></chat-record> </div> <div></div> </div> </div>

对于聊天记录的气泡展示,与数据类型相关性很强,ChatRecord组件只关心对数据的处理和展示,我们可以完全不用关心消息的发送、接收、音频的录制、停止录制、接受音频等逻辑,只需要根据数据来展示不同的样式即可。
这样Vue的响应式就充分获得了用武之地:无需用代码对样式展示进行控制,只需要设计合理的数据格式和样式模板,然后注入不同的数据即可。
模板页面: 使用v-if控制,修改chatList里的对象内容即可改变页面展示。

根据业务需求,将ChatRecord可能接收到的数据分为以下几类:

发送方为自己:

文本输入,显示文本
实现简单,不做赘述。

语音输入 Loading状态,显示波纹动画和计时

正在输入


该动画使用CSS实现,参考地址: https://www.cnblogs.com/lhb25/p/loading-spinners-animated-with-css3.html

计时器使用JS的setInterval方法,每100ms更新一次录制时长

this.recordTimer = setInterval(() => { this.audioDuration = this.audioDuration + 0.1 }, 100)

停止后清空计时器:

clearInterval(this.recordTimer)

语音输入完毕,根据录制的语音,绘制波纹
效果:

绘制出真实的波形

使用wavesurfer插件:

initWaveSurfer() { this.$nextTick(() => { this.wavesurfer = WaveSurfer.create({ container: this.$refs.waveform, height: 20, waveColor: '#3d6fff', progressColor: 'blue', backend: 'MediaElement', mediaControls: false, audioRate: '1', fillParent: false, maxCanvasWidth: 500, barWidth: 1, barGap: 2, barHeight: 5, barMinHeight: 3, normalize: true, cursorColor: '#409EFF' }) this.convertAudioToUrl(this.waveAudio).then((res) => { this.wavesurfer.load(res) setTimeout(() => { this.audioDuration = this.getAudioDuration() }, 100) }) }) }, // 将音频转化成url地址 convertAudioToUrl(audio) { let blobUrl = '' if (this.data.sendBy === 'self') { blobUrl = window.URL.createObjectURL(audio) return new Promise((resolve) => { resolve(blobUrl) }) } else { return this.base64ToBlob({ b64data: audio, contentType: 'audio/wav' }) } }, base64ToBlob({ b64data = '', contentType = '', sliceSize = 512 } = {}) { return new Promise((resolve, reject) => { // 使用 atob() 方法将数据解码 let byteCharacters = atob(b64data) let byteArrays = [] for ( let offset = 0; offset < byteCharacters.length; offset += sliceSize ) { let slice = byteCharacters.slice(offset, offset + sliceSize) let byteNumbers = [] for (let i = 0; i < slice.length; i++) { byteNumbers.push(slice.charCodeAt(i)) } // 8 位无符号整数值的类型化数组。内容将初始化为 0。 // 如果无法分配请求数目的字节,则将引发异常。 byteArrays.push(new Uint8Array(byteNumbers)) } let result = new Blob(byteArrays, { type: contentType }) result = Object.assign(result, { // 这里一定要处理一下 URL.createObjectURL preview: URL.createObjectURL(result), name: `XXX.wav` }) resolve(window.URL.createObjectURL(result)) }) },

发送方为系统:

仅返回文本:显示文本

仅返回音频(参考发送方为自己的实现)

绘制波形

返回文本,随即返回文本对应的合成音频,显示文本和播放按钮

状态,显示播放按钮

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjzws.html