日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

瀏覽:29日期:2022-10-25 14:20:46

引言

在與實現了語音合成、語義分析、機器翻譯等算法的后端交互時,頁面可以設計成更為人性化、親切的方式。我們采用類似于聊天對話的實現,效果如下:

智能客服(輸入文本,返回引擎處理后的文本結果)

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

語音合成(輸入文本,返回文本以及合成的音頻)

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

如上圖所示,返回文本后,再返回合成出的音頻。音頻按鈕嵌在對話氣泡中,可以點擊播放。

語音識別(在頁面錄制語音發送,頁面實時展示識別出的文本結果)

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

實現功能及技術要點

1、基于WebSocket實現對話流頁面與后端的交互是實時互動的,所以采用WebSocket協議,而不是HTTP請求,這樣后端推送回的消息可以實時顯示在頁面上。WebSocket的返回是隊列的、無序的,在后續處理中我們也需要注意這一點,在后文中會說到。2、調用設備麥克風進行音頻錄制和轉碼加頭,基于WebAudio、WaveSurferJS等實現音頻處理和繪制3、基于Vue的響應式頁面實現4、CSS3 + Canvas + JS 交互效果優化

錄制音頻CSS動畫效果 聊天記錄自動滾動

下面給出部分實現代碼。

集成WebSocket

我們的聊天組件是頁面側邊打開的抽屜(el-drawer),Vue組件會在打開時創建,關閉時銷毀。在組件中引入WebSocket,并管理它的開、關、消息接收和發送,使它的生命周期與組件一致(打開窗口時創建ws連接,關閉窗口時關閉連接,避免與后臺連接過多。)

created(){ if (typeof WebSocket === ’undefined’) { alert(’您的瀏覽器不支持socket’) } else { // 實例化socket this.socket = new WebSocket(this.socketServerPath) // 監聽socket連接 this.socket.onopen = this.open // 監聽socket錯誤信息 this.socket.onerror = this.error // 監聽socket消息 this.socket.onmessage = this.onMessage this.socket.onclose = this.close }}destroyed(){ this.socket.close()}

如上,將WebSocket的事件綁定到JS方法中,可以在對應方法中實現對數據的接收和發送。打開瀏覽器控制臺,選中指定的標簽,便于對WebSocket連接進行監控和查看。

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

音頻錄制采集

從瀏覽器端音頻和視頻采集基于網頁即時通信(Web Real-TimeCommunication,簡稱WebRTC) 的API。通過WebRTC的getUserMedia實現,獲取一個MediaStream對象,將該對象關聯到AudioContext即可獲得音頻。

可參考RecorderJS的實現: https://github.com/mattdiamond/Recorderjs/blob/master/examples/example_simple_exportwav.html

if (navigator.getUserMedia) { navigator.getUserMedia( { audio: true }, // 只啟用音頻 function(stream) { var context = new(window.webkitAudioContext || window.AudioContext)() var audioInput = context.createMediaStreamSource(stream) var recorder = new Recorder(audioInput) }, function(error) { switch (error.code || error.name) { case ’PERMISSION_DENIED’: case ’PermissionDeniedError’: throwError(’用戶拒絕提供信息。’) break case ’NOT_SUPPORTED_ERROR’: case ’NotSupportedError’: throwError(’瀏覽器不支持硬件設備。’) break case ’MANDATORY_UNSATISFIED_ERROR’: case ’MandatoryUnsatisfiedError’: throwError(’無法發現指定的硬件設備。’) break default: throwError(’無法打開麥克風。異常信息:’ + (error.code || error.name)) break } } ) } else { throwError(’當前瀏覽器不支持錄音功能。’) }

注意: 若navigator.getUserMedia獲取到的是undefined,是Chrome瀏覽器的安全策略導致的,需要通過https請求或配置瀏覽器,配置地址: chrome://flags/#unsafely-treat-insecure-origin-as-secure

瀏覽器采集到的音頻為PCM格式(PCM (脈沖編碼調制 Pulse Code Modulation)),需要對音頻加頭才能在頁面上進行播放。注意加頭時采樣率、采樣頻率、聲道數量等必須與采樣時相同,不然加完頭后的音頻無法解碼。參考查看https://github.com/mattdiamond/Recorderjs/blob/master/src/recorder.js中exportWav方法。

業務中對接的語音識別引擎為實時轉寫引擎,即:不是錄制完成后再發送,而是一邊錄制一邊進行編碼并發送。使用onaudioprocess方法監聽語音的輸入:

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

參考這個實現,我們可以在每次監聽到有數據寫入時,從buffer中獲取到錄制到的數據,并進行編碼、壓縮,再通過WebSocket發送。

Vue組件設計和業務實現

分析頁面業務邏輯,將代碼拆分成兩個組件:ChatDialog.vue 聊天對話框頁面,根據輸入類型,分為文本輸入、語音輸入。ChatRecord.vue聊天記錄組件,根據發送方(自己或者系統)展示向左/向右的氣泡,根據內容顯示文本、音頻等。ChatDialog是ChatRecord的父組件,遍歷ChatDialog中的chatList對象(Array),將chatList中的項注入到ChatRecord中。

<div class='chat-list'> <div v-for='(item,index) in chatList' :key='index' class='msg-wrapper'> <chat-record ref='chatRecord' :data='item' @showJson='showJsonDialog'></chat-record> </div> <div style='height:0px; overflow:hidden'></div> </div></div>

對于聊天記錄的氣泡展示,與數據類型相關性很強,ChatRecord組件只關心對數據的處理和展示,我們可以完全不用關心消息的發送、接收、音頻的錄制、停止錄制、接受音頻等邏輯,只需要根據數據來展示不同的樣式即可。這樣Vue的響應式就充分獲得了用武之地:無需用代碼對樣式展示進行控制,只需要設計合理的數據格式和樣式模板,然后注入不同的數據即可。模板頁面: 使用v-if控制,修改chatList里的對象內容即可改變頁面展示。

根據業務需求,將ChatRecord可能接收到的數據分為以下幾類:

發送方為自己:

文本輸入,顯示文本

實現簡單,不做贅述。

語音輸入 Loading狀態,顯示波紋動畫和計時

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

該動畫使用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)

停止后清空計時器:

語音輸入完畢,根據錄制的語音,繪制波紋

效果:

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

使用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)) }) },

發送方為系統:

僅返回文本:顯示文本 僅返回音頻(參考發送方為自己的實現)

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

返回文本,隨即返回文本對應的合成音頻,顯示文本和播放按鈕

Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例

頁面嵌入audio標簽,將hidden設置為true使其不顯示:

<div class='audio-player'> <svg-icon v-if='!isPlaying' icon-class=’play’ @click='onClickAudioPlayer' /> <svg-icon v-else icon-class=’pause’ @click='onClickAudioPlayer' /> <audio :src='http://m.b3g6.com/bcjs/playAudioUrl' autostart='true' hidden='true' ref='audioPlayer' /> </div>

playAudioUrl的生成參考上面生成的wavesurfer的url。使用isPlaying參數記錄當前音頻的播放狀態,并使用setTimeout方法,當播放了音頻時長后,將播放按鈕自動置為play。

onClickAudioPlayer() { if (this.isPlaying) { this.$refs.audioPlayer.pause() this.isPlaying = false } else { // 每次點擊時,開始播放,并在播放完畢將isPlaying置為false this.$refs.audioPlayer.currentTime = 0 this.$refs.audioPlayer.play() this.isPlaying = true setTimeout(() => { // 將正在播放重置為false this.isPlaying = false }, Math.ceil(this.$refs.audioPlayer.duration) * 1000) } },

聊天記錄自動定位到最后一條:

使用scrollIntoView()方法

記錄每次會話對應的記錄ID(recordId):

定義單次會話的id,并在返回的消息中回傳,從而建立多條websocket返回的關聯關系。

以上就是全部實現。難點主要是請求麥克風權限和對音頻進行編碼,在加wav頭時必須保證和采樣時的采樣率、頻率一致 。

以上就是Vue +WebSocket + WaveSurferJS 實現H5聊天對話交互的實例的詳細內容,更多關于vue 實現H5聊天對話交互的資料請關注好吧啦網其它相關文章!

標簽: Vue
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
首页国产精品| 亚洲图片久久| 国产精品视频一区视频二区| 亚洲黄页一区| 亚洲在线网站| 国产一区亚洲| 国产午夜精品一区二区三区欧美 | 日韩av网站免费在线| 久久久水蜜桃av免费网站| 青青国产精品| 亚洲免费激情| 高清一区二区三区av| 国产精品手机在线播放| 视频一区二区三区中文字幕| 欧美日韩在线二区| 久久久久国产一区二区| 免费精品国产的网站免费观看| 国产91在线精品| 老色鬼精品视频在线观看播放| 国产精品美女午夜爽爽| 蜜桃视频第一区免费观看| 亚洲一区二区三区免费在线观看 | 一区二区精彩视频| 在线午夜精品| 日韩高清在线观看一区二区| 麻豆一区二区99久久久久| 欧美激情福利| 日韩精品1区| 黄色日韩在线| 国产日韩欧美| 蜜桃精品在线| 日韩专区欧美专区| 麻豆精品久久| 色乱码一区二区三区网站| 日韩av免费| 亚洲毛片在线免费| 91亚洲成人| 五月天av在线| 国产精品7m凸凹视频分类| 久久亚洲精品伦理| 日韩av二区| 日韩专区欧美专区| 精品国产乱码久久久| 香蕉久久久久久久av网站| 麻豆网站免费在线观看| 日韩精品一二区| 成人国产精选| 91精品丝袜国产高跟在线| 亚洲激情国产| 日韩伦理福利| 久久国产人妖系列| 日韩一区二区免费看| 91精品美女| 成人精品天堂一区二区三区| 最新国产精品久久久| 日韩一区二区三区免费| 亚洲日本免费电影| 秋霞影视一区二区三区| 国产精品一区二区99| 免费视频一区二区| 亚洲黄色中文字幕| 国产精品99久久免费观看| 亚洲精品乱码久久久久久蜜桃麻豆| 色一区二区三区四区| 四虎国产精品免费久久| 国内精品福利| 免费黄色成人| 色婷婷精品视频| 国产精品久久久网站| 999在线观看精品免费不卡网站| 精品成人免费一区二区在线播放| 国产精品对白| 日韩在线网址| 亚洲精品美女91| 日本少妇一区二区| 久久狠狠久久| 日韩一区二区三区高清在线观看| 一区二区三区网站| 日韩精品欧美成人高清一区二区| 亚洲精选av| 合欧美一区二区三区| 三级一区在线视频先锋| 日韩三级一区| 欧美激情aⅴ一区二区三区| 国产亚洲综合精品| 亚洲精品在线国产| 欧美xxxx性| 91成人超碰| 日韩精品三区四区| 美腿丝袜亚洲一区| 日韩在线高清| 巨乳诱惑日韩免费av| 国产精品亚洲欧美日韩一区在线 | 国产探花在线精品一区二区| 青青国产精品| 欧美精品第一区| 激情六月综合| 麻豆成人在线| 国产一区二区三区不卡av| 精品国产91| av亚洲在线观看| 日韩av中文字幕一区二区| 丰满少妇一区| 日韩在线黄色| 亚洲午夜久久久久久尤物| 日本欧美韩国一区三区| 久久精品欧美一区| 国产免费播放一区二区| 亚洲国内欧美| 国产激情欧美| 亚洲精品无吗| 岛国av免费在线观看| 蜜臀久久99精品久久一区二区| 成人日韩av| 色综合视频一区二区三区日韩| 国产a亚洲精品| 日韩三级久久| 在线视频免费在线观看一区二区| 国产精品亚洲产品| 日本大胆欧美人术艺术动态| av资源中文在线| 国产精品亚洲综合在线观看| 婷婷激情久久| 国产综合色区在线观看| 国产精品二区影院| 日韩不卡一区二区三区| 久久国产亚洲精品| 精品一区91| 国产福利资源一区| 国产精品欧美一区二区三区不卡 | 在线亚洲精品| 久久久久久久久99精品大| 国内精品麻豆美女在线播放视频| 国产精品免费99久久久| 日韩精品社区| 欧美伊人久久| 国产精品资源| 国产精品任我爽爆在线播放| 亚洲一区二区三区高清| 香蕉久久久久久久av网站| 视频一区欧美日韩| 视频一区二区中文字幕| 亚洲三级精品| 7777精品| 国产成年精品| 久久久久99| 99精品美女| 亚洲精品在线影院| 成人av动漫在线观看| 丝袜亚洲精品中文字幕一区| 亚洲aa在线| 日本午夜精品久久久久| 国产精品九九| 精品免费av在线| 亚洲一区二区三区久久久| 欧美日韩一视频区二区| 91精品国产自产在线丝袜啪| 精品三级av| 免费国产自久久久久三四区久久| 热久久国产精品| 在线精品福利| 久久精品免视看国产成人| 激情欧美国产欧美| 日韩成人午夜精品| 中文字幕在线官网| 日韩avvvv在线播放| av资源新版天堂在线| 在线亚洲精品| 麻豆视频一区二区| 亚洲精品一二三区区别| 亚洲麻豆一区| 四虎成人av| 青草av.久久免费一区| 中文一区一区三区高中清不卡免费| 日本欧洲一区二区| av资源新版天堂在线| 婷婷成人av| 在线亚洲国产精品网站| 天堂а√在线最新版中文在线| 日韩午夜免费| 电影天堂国产精品| 麻豆精品新av中文字幕| 国产亚洲高清视频| 一本大道色婷婷在线| 国产精品天堂蜜av在线播放| 久久亚洲国产精品一区二区| 国产乱码午夜在线视频| 久久精品 人人爱| 欧美日韩在线二区| 中文字幕成在线观看| 精品视频黄色| 成人在线超碰| 欧美伊人影院| 免费在线观看不卡| 免费欧美在线视频| 亚洲欧美日韩视频二区| 亚洲一区二区三区高清| 日韩网站中文字幕| 日韩理论片av| 日韩电影免费在线观看|