轉發鏈接:https://segmentfault.com/a/1190000022268377
前言
前端消息的實時推送我相信很多人不陌生,我們可以想到利用WebSocket,服務端主動向客戶端推送數據,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。其優點有很多,能更好的節省服務器資源和帶寬,並且能夠更實時地進行通訊等等。語音播報則能夠在人們視覺沒有來的及關注時侯,通過聽覺來獲取需要信息。
這篇文章主要介紹的是基於websocket,利用Stomp.js以及HTML5語音Web Speech API——SpeechSynthesis來實現前端消息的實時推送與語音播報。
StompJS
讓我們先了解一下STOMP(the Simple (or Streaming) Text Orientated Messaging Protocol)——面向消息(或流)的簡單文本協議。它提供了一個可互操作的連接格式,允許STOMP客戶端與任意STOMP消息代理(Broker)進行交互。
WebSocket的實現客戶端看起來比較簡單,但是需要與後臺進行很好的配合和調試才能達到最佳效果。通過SockJS 、Stomp來進行瀏覽器兼容,可以增加消息語義和可用性。簡而言之,WebSocket 是底層協議,SockJS 是WebSocket 的備選方案,也是底層協議,而 STOMP 是基於 WebSocket(SockJS)的上層協議。
創建STOMP客戶端
下面簡單的介紹一下常用的方法。在web瀏覽器中我們可以通過兩種方式進行客戶端的創建:1、使用普通的WebSocket
<code>let url = "ws://localhost:61614/stomp";
let client = Stomp.client(url);/<code>
2、使用定製的WebSocket如果需要使用其他類型的Websocket(例如由SockJS包裝的Websocket),就利用下面的方式創建客戶端
<code>let url = "ws://localhost:61614/stomp";
let socket = new SockJS(url);
let client = Stomp.over(socket);/<code>
除上面的客戶端創建方式不同外,後續的連接等操作都是一樣的。
連接服務端
我們可以用client.connect()方法來連接服務端
<code>client.connect(login,passcode,successCallback,errorCallback);/<code>
其中login和passcode都是字符串,相當於是用戶的登錄名和密碼憑證。successCallback為連接成功的回調函數,errorCallback為連接失敗的回調函數。還可以這樣寫:
<code>client.connect({
login:'name',
passcode:'666',
'token':'2333'
},successCallback,errorCallback);/<code>
斷開連接:
<code>client.disconnect(function(){console.log("再見")})/<code>
Heart-beating(心跳)
heart-beating也就是消息傳送的頻率,incoming是接收頻率,outgoing是發送頻率,其默認值都為10000ms,我們可以手動設置:
<code>client.heartbeat.outgoing = 5000;
client.heartbeat.incoming = 0;/<code>
發送消息
客戶端向服務端發送消息利用send()方法,此方法有三個參數:第一個參數(string)必需,為發送消息的目的地;第二個參數(object)可選,包含了額外的頭部信息;第三個參數(string)可選,為發送的消息。
<code>client.send(destination, {}, body);/<code>
訂閱消息
訂閱消息也就是客戶端接收服務端發送的消息,訂閱消息可以利用subscribe()方法,此方法有三個參數:第一個參數(string)必需,為接收消息的目的地;第二個參數必需為回調函數;第三個參數{object}為可選,包含額外的頭部信息。
<code>client.subscribe(destination, callback, {});/<code>
取消訂閱消息可以利用unsubscribe()方法:
<code> let mySubscribe = client.subscribe;
mySubscribe.unsubscribe();/<code>
客戶端訂閱消息可以訂閱廣播,如下所示:
<code>client.subscribe('/topic/msg',function(messages){
console.log(messages);
})/<code>
也可以進行一對一消息的接收:
<code>//第一種方式
const userId = 666;
client.subscribe('/user/' + userId + '/msg',,function(messages){
console.log(messages);
})
//第二種方式
client.subscribe('/msg',function(messages){
console.log(messages);
}, {"userId ": userId })/<code>
客戶端採用的寫法要根據服務端代碼來做選擇。
Web Speech API
在HTML5中,與語音相關的Web Speech API可以分為兩種:一種為語音識別(Speech Recognition),另一種為語音合成(Speech Synthesis)。他們的作用分別為“語音轉文字”和“文字轉語音”。既然是HTML5中的東西,我們還是要先看看他們的兼容性如何:Speech Recognition:
Speech Synthesis:
從上面的圖中可以看出:語音識別(Speech Recognition)很慘烈,大部分瀏覽器還不支持。語音合成(Speech Synthesis)除開IE和Opera,基本上都支持了。本文要實現的是語音播報,就是要把文字消息,轉成語音播報出來,而語音合成(Speech Synthesis)就是實現這樣的功能,而且兼容性也是不錯的,所以我們就能拿來使用啦~
SpeechSynthesis
語音識別(Speech Recognition)就不過多介紹了,我們來詳細看看語音合成(Speech Synthesis)。我們可以先把下面這段代碼打到瀏覽器的控制檯上:
<code>let speechInstance = new window.SpeechSynthesisUtterance('你好,可以交個朋友嗎');
window.speechSynthesis.speak(speechInstance);/<code>
不出意外,瀏覽器說話了,說明瀏覽器是支持這個API的。下面簡單介紹一下相關的屬性和方法:SpeechSynthesisUtterance對象的屬性:
屬性類型描述textstring需要要讀的內容langstring使用的語言(比如:"zh-CN")volumenumber音量,值在0-1之間(默認是1)ratenumber語速的倍數,值在0.1-10之間(默認1倍)pitchnumber音高,值在0-2之間,(默認是1)voicestring指定希望使用的聲音
SpeechSynthesisUtterance對象的方法:
方法描述onstart語音開始合成時觸發onpause語音暫停時觸發onresume語音合成重新開始時觸發onend語音結束時觸發
上述定義的speechInstance其實是我們創建的文本實例,而真實的語音是由speechSynthesis來創建的,其常用的方法如下:
方法描述speak()開始合成語音,將對應的實例添加到語音隊列中cancel()停止合成語音,刪除隊列中所有的語音pause()暫停語音合成resume()恢復暫停後的語音getVoices()返回瀏覽器所支持的語音包數組
實戰環節
上面介紹了StompJS和SpeechSynthesis常用的屬性和方法,是時候動手碼一碼了。
前端web頁面消息實時接收
模擬服務端發送消息
想要接收實時消息,我們當先然要有消息的來源對不對。消息是從後臺發來的,一般是利用Java,然後結合ActiveMQ或者RabbitMQ等消息中間件,Java代碼就不多說,我們接下來就利用ActiveMQ來模擬服務端向客戶端發送消息。這裡說一說windows環境下吧,首先要下載ActiveMQ,直接點擊官網:http://activemq.apache.org/do...選擇最新發佈下來的壓縮包,解壓即可,然後進入解壓後的bin目錄,可以看見裡面有兩個文件夾,win32和win64,這個就根據自己電腦的操作系統來選擇,點擊進去,再雙擊activemq.bat啟動,如果看見下面這樣就說明啟動成功:
如果沒有啟動成功,那多半是因為沒有jdk,點這裡,跟著安裝就歐克啦~安裝完畢後,再雙擊activemq.bat,現在我們就啟動成功了。在瀏覽器中輸入:http://localhost:8161,可以看到:
點擊Manage ActiveMQ broker,用戶名和密碼都是admin,然後再點擊Topics
在輸入框中輸入msg,然後點擊create按鈕
可以看見下方列表中多了一個Name為msg的Topics
其中需要注意的是下面幾項,以msg為例:
- Number Of Consumers :消費者數量,相當於連接服務端msg的客戶端的數量;
- MessagesEnqueued:進入隊列的消息,相當於服務端向客戶端發送的消息數量;
- MessagesDequeued:出了隊列的消息,相當於客戶端消費(訂閱)掉的消息數量。
- 其它想要多瞭解的可以搜索一波。
模擬“服務端”準備就緒。
客戶端消息接收
這“服務端”搞好了,接下來就是客戶端的實現,代碼貼出來index.html:
<code>
<title>實時語音播報/<title>
/<code>
在瀏覽器中打開這個HTML文件,然後打開控制檯,可以看見,我們已經連接服務端成功了:
連接成功之後呢,我們就可以嘗試在服務端向客戶端發送消息,先切換到ActiveMQ 的頁面:
可以看見我們的消費者的數量為1了,然後點擊Send To,就可以開始發消息了:
比如我們發送一個“你好”,然後我們再切到index.html:
我們收到了來自服務端的問候~
SpeechSynthesis語音播報
消息已經能夠實時接收了,現在就是需要把接收到的消息讀出來,思路很簡單,就是把語音合成相關API封裝成一個函數,然後當我們服務端發送消息到客戶端之後,把消息數據傳到為我們定義好的語音播報函數里面,然後就能讀出我們服務端發出的消息了,說幹就幹:
<code>//語音播報
speechInfo = () => {
let speechInstance = new SpeechSynthesisUtterance();
return {
//語音播報開始
start: function (content) {
let lang = 'zh-CN';
let text = content;
if( text !== '') {
speechInstance.text = text;
speechInstance.lang = lang;
speechInstance.volume = 1;
speechInstance.rate = 1;
speechSynthesis.speak(speechInstance);
speechInstance.onend = function(event){
console.log("語音播報完畢");
}
}
},
//暫停
pause : function () {
speechSynthesis.pause();
},
//重新開始
resume: function() {
speechSynthesis.resume();
},
//取消
cancel: function() {
speechSynthesis.cancel();
}
}
};/<code>
那咱們調用一下,然後在ActiceMQ頁面發送一條新消息,看是不是如我們所願:
<code>...
client.subscribe('/topic/msg', function(message){
if(message.body){
data = message.body;
console.log(message.body);
//調用語音合成函數
speechInfo().start(data);
}
})
.../<code>
如果是火狐,360安全瀏覽器等瀏覽器,我們的消息和聲音都如期而至。
但是如果用的是Chrome的話,很難受,並沒有聲音,難道是Chrome不支持了嗎?但是我們之前那兩行測試代碼說明Chrome是支持SpeechSynthesis的,那是怎麼回事?答案就在下面:
Chrome不再支持SpeechSynthesis.speak()的自動播放,要想用的話,必須用戶手動去調用它。原因可以看這裡被垃圾廣告濫用後谷歌瀏覽器71將限制語音合成自動播放
垃圾網站出來背鍋!!!
想不到吧,有一天需要去“兼容”Chrome了。語音不能實時的播報出來,我們看看有沒有什麼辦法。我的思路是在頁面加一個按鈕,然後進行模擬人去點擊,完整index.html代碼:
<code>
<title>Document/<title>
<button> 點擊/<button>
/<code>
但是我們能想到,谷歌想不到?這裡又涉及一個知識點:isTrusted
Event接口的isTrusted是一個Boolean類型的只讀屬性.當事件由用戶操作生成時為true,由腳本創建或修改,或通過調用EventTarget.dispatchEvent生成,為false
我們在控制檯代碼中打個斷點,然後在ActiveMQ 發條消息瞧一瞧,是不是與這個有關:
可以看見isTrusted的值為false,這個模擬點擊事件是不被瀏覽器信任的,然後我們再手動點擊一下我們寫好的按鈕:
isTrusted的值為true,我們的聲音也出來了,當我們再在ActiveMQ 發送一條消息:
聲音自動播放出來了。
最後
需要提一點的是,我在實際是在react中開發的,相關方法都和上述的寫法類似,但是卻不會觸發Chrome對於SpeechSynthesis.speak()的限制,這個限制也是我在寫這篇文章的時候,用原生js+HTML的時候發現的。總的來說用原生js+HTML實現的並不算完美,在Chrome下需要在頁面加載完成後進行一次點擊,才能把後續的語音實時的播報出來。如果大家有相關的解決辦法,或者用其它的方式實現了前端消息實時的語音播報,歡迎提出來,先謝過了~
轉發鏈接:https://segmentfault.com/a/1190000022268377
閱讀更多 Echa攻城獅 的文章