本人在雙十一期間,做的一個移動端互動項目中,遇到一個在 App 、微信、H5頁面環境切換選擇音頻播放的功能,在測試的時候出了不少兼容性問題,這里有很多值得探索的知識,今天我們就來看一下這個 HTML5-Audio。
Audio 標簽用於定義聲音,比如音樂或其他音頻流,HTML5 的 Audio 標簽在很大程度上取代了 Flash 來播放音樂。
一、默認樣式
Audio 標簽在瀏覽器中的默認樣式如下圖所示,需要注意的一個地方:需要配置 controls 屬性(是否顯示默認控制條,是 Audio 標簽的控制屬性),否則不展示樣式效果。
二、Audio 支持的音頻文件格式
1、OGG:
OGG 是一種新的音頻壓縮格式,類似於 MP3 等的音樂格式。OGG 是完全免費、開放和沒有專利限制的。OGG 文件格式可以不斷地進行大小和音質的改良,而不影響舊有的編碼器或播放器。
2、MP3:
MP3 是一種音頻壓縮技術,其全稱是 MovingPictureExpertsGroupAudioLayerIII(動態影像專家壓縮標准音頻層面3),簡稱為 MP3 。它被設計用來大幅度地降低音頻數據量。將音樂以 1:10 甚至 1:12 壓縮成容量較小的文件,而對於大多數用戶來說重放的音質與最初的不壓縮音頻相比沒有明顯的下降。
3、WAV:
WAV 是微軟公司開發的一種聲音文件格式,它用於保存 Windows 平台的音頻信息資源,被 Windows 平台及其應用程序所廣泛支持,標准格式化的 WAV 文件和 CD 格式一樣,因此在聲音文件質量和 CD 相差無幾。
三、兼容性問題
我們來從下面幾個角度看兼容性問題:
1、音頻格式的兼容性
音頻格式 | Chrome | Firefox | IE9 | Opera | Safari |
---|---|---|---|---|---|
OGG | 支持 | 支持 | 支持 | 支持 | 不支持 |
MP3 | 支持 | 不支持 | 支持 | 不支持 | 支持 |
WAV | 不支持 | 支持 | 不支持 | 支持 | 不支持 |
說明:
Audio 標簽默認支持的主流的音頻文件格式有 MP3、WAV、OGG ,不同的瀏覽器對三種格式支持程度不一樣。其中 MP3 格式支持度最好。
WAV 格式音質最好,但是文件體積較大。MP3 壓縮率較高,普及率高,音質相比 WAV 要差。OGG 與 MP3 在相同位速率(Bit Rate)編碼的情況下,OGG 體積更小,並且 OGG 是免費的不用交專利費(這點國人很中意)。
2、不同瀏覽器對於 HTML5 Audio 標簽的兼容性
說明:
在版本較新的瀏覽器中都是可以支持 Audio 標簽的,在移動端上兼容性會更好一些。
四、使用方法
我們經常會使用到的屬性、方法、事件:
1、常用屬性:
屬性 | 屬性值 | 注釋 |
---|---|---|
src | url | 播放的音樂的 url 地址(火狐只支持 OGG的音樂,而 IE9 只支持 MP3 格式的音樂。chrome 貌似全支持) |
preload | preload | 預加載(在頁面被加載時進行加載或者說緩沖音頻),如果使用了 autoplay 的話那么該屬性失效。 |
loop | loop | 循環播放 |
controls | controls | 是否顯示默認控制條(控制按鈕) |
autoplay | autoplay | 自動播放 |
2、常用方法:
函數 | 作用 |
---|---|
load() | 加載音頻、視頻軟件 |
play() | 加載並播放音頻、視頻文件或重新播放暫停的的音頻、視頻 |
pause() | 暫停當前播放的音頻 |
3、常用事件:
事件名稱 | 事件作用 |
---|---|
play | play 和 autoplay 播放時 |
pause | pause 方法觸發時 |
ended | 當前播放結束 |
timeupdate | 當前播放時間發生改變的時候(播放中常用的時間處理) |
canplaythrough | 歌曲已經載入完全完成 |
canplay | 緩沖至目前可播放狀態。 |
我們可以通過 JS 代碼來控制播放:
let audio = document.getElementById('audio');
audio.load();//加載
audio.play();//播放
audio.pause();//暫停
audio.loop = true;//循環播放
五、問題與解決方案:
好的,我們再來看具體看一下 Audio 標簽的兼容性問題:
1、如何解決預加載問題
現狀:preload 在 iOS 系統上的 safari 和微信是不支持的。
解決方法:需要用戶主動觸發事件(Event)才能進行播放。
示例代碼:
$("#btn").click(function(){
$("#audio").load();
})
2、如何解決多個音頻文件切換問題
現狀:當時項目中有切換不同音樂播放,如果采用更改 Audio 標簽的 src 的方式,iOS 下會出現不能播放音樂或者播放延遲太高問題。
解決方法:這種 bug 出現的原因是音頻文件不能緩存在 iOS 系統上,每當頁面訪問其他音頻文件時,都從網絡訪問音頻文件,解決方法:可以在頁面中聲明多個 Audio 標簽,把需要引入的音頻文件預先引入,播放哪個再調用相應文件,這個方案的缺點是在 iOS 系統下每一個 Audio 占一個線程,如果有多個的 Audio ,則很占資源;或者把多個音頻文件合並成為一個文件,播放其他音頻的時候只需要調用合並之后的音頻文件的相應時段,雖然比較繁瑣,但是兼容性很好,可以參考下該音頻合並工具(http://jsfiddle.net/aarongloege/rQv5h/light/)。
示例代碼:
<audio src="" id="audio1"></audio>
<audio src="" id="audio2"></audio>
var audio1 = document.getElementById('audio1');
var audio2 = document.getElementById('audio2');
audio1.play();
audio2.play();
3、如何解決自動播放問題
現狀:iOS 端 safari 瀏覽器或者部分安卓手機的瀏覽器不支持 autoplay 屬性。
解決方法:還是引導用戶手動觸發播放操作。
比如綁定 touchstart 事件進行 audio.play() 操作,因此在和產品、測試同學溝通確認的時候就需要確認好這個點。如果在微信環境下可以調用微信提供的插件( jweixin-1.0.0.js )。
示例代碼:
wx.ready(function() {
audio.play();
});
4、如何解決播放數量問題
現狀:一個 Audio 標簽只能播放一個音頻。
解決方法:如果要同時播放多個音頻,只得使用多個 Audio 標簽,但是這個情況下要注意,各瀏覽器是是支持在同一頁面播放多個音頻的,而項目場景基本只允許播放一個音頻,這個得注意控制播放當前音樂時要停止其他音樂項的播放。
示例代碼:
<audio src="a.mp3" src="a.mp3"></audio>
<audio src="b.mp3" src="b.mp3"></audio>
<audio src="c.mp3" src="c.mp3"></audio>
5、如何解決移動端下音頻混合播放問題
現狀:在 iOS safari 下關閉瀏覽器窗口(切換至后台)或者切換標簽時, Audio 仍然繼續循環播放音頻文件,如果關閉瀏覽器標簽才會停止播放。
解決方法:使用循環存儲時間來檢查用戶是否在網頁上, timeupdate 事件是在音頻 Audio 的播放位置發生改變時觸發。
示例代碼:
var lastSeen;
var loop = function (){
lastSeen = Date.now();
setTimeout(loop, 50);
};
loop();
var music = document.getElementById('music');
music.addEventListener('timeupdate', function (){
if(Date.now() - lastSeen > 100){
this.pause();
}
}, false);
6、如何解決續播問題
現狀:在 JDApp 端頁面音頻播放時,切換至后台或者其他應用,音樂暫停播放之后,再切換至音樂播放的原頁面,音樂沒有繼續播放。
解決方法:判斷是否是在 JDApp 下,如果在 JDApp 使用 JDAppUnite 調用原生方法 callRouterModuleWithParams 控制音頻繼續播放。
示例代碼:
function toOriginalChk(wvt, soundRouter){
// 判斷環境
if (wvt == 'notJdApp') {
return;
} else if (wvt == 'wk') {
// 通知原生播放狀態
window.webkit.messageHandlers.JDAppUnite.postMessage({
'method': 'callRouterModuleWithParams',
'params': JSON.stringify(soundRouter)
});
} else if (wvt == 'ui') {
window.JDAppUnite && window.JDAppUnite.callRouterModuleWithParams(JSON.stringify(soundRouter));
}
}
7、如何解決初始化延遲問題
現狀:在 iOS safari 瀏覽器初始化一個新的音頻流時會有幾秒的延時。
解決方法:出現這 bug 的原因是因為 iOS 需要實例化一個新的音頻對象,再通過網絡請求音頻資源,音頻資源加載完畢之后才能進行播放,解決方案:在頁面 ready 之后把每個文件都 load 一下,然后再調用 play 方法,這么做可以使音頻資源做預加載,提前請求網絡,可以具體業務場景來優化使用。
示例代碼:
$(function(){
$("#audio1").load();
$("#audio2").load();
})
8、如何解決靜音操作問題
現狀:理想情況下用戶可以在相關頁面通過觸發事件實現音頻的靜音操作
解決方法:可以通過設置 muted 屬性來設置 Audio 靜音,但是在 iOS 8及其以下 或者 IE9 及其以下版本不支持 muted 屬性。
示例代碼:
$("#audio").muted = true
9、如何解決加載問題
現狀:如果在頁面未加載完成時調用 play 方法會失敗,在這種情況下設置 currentTime 會拋出異常錯誤Failed to load resource: the server responded with a status of 404 ()。
解決方法:遇到此類問題,可以先檢查網絡及音頻大小以排除。
10、如何解決循環播放問題
現狀:在 iOS 系統在 5 之前不支持循環屬性。
解決方法:可以通過向 onEnded 事件添加了一個事件偵聽程序,並在該函數中調用 play 方法解決。
示例代碼:
var audio = document.getElementById('audio');
audio.play();
var onEnded = function() {
this.play();
};
audio.addEventListener('ended', onEnded, false);
小tip:HTML5 Audio 標簽給我們提供了一些額外的信息來指定播放哪一時間段,方法是在媒體文件后面跟隨(“#”)格式
<audio src="audio.mp3#t=10,20"></audio>
// src="audio.mp3#t=10,20" (從10s播放到20s)
// src="audio.mp3#t=10" (從10s播放到完)
// src="audio.mp3#t=,10" (從開頭播放到10s)
總結
HTML5 提供的 Audio 標簽雖然有不少的兼容性問題, 但是在移動端使用音頻播放, Audio 仍然是 HTML5 發展的大勢所趨,值得我們使用。本文介紹了 Audio 標簽的使用方法以及音頻文件的兼容版本和 Audio 在不同環境下的兼容性問題並相應給出了解決方案。最后附上自己開發的插件
(https://github.com/jdf2e/audioCreate.js/tree/master/audiojs),
掃二維碼可訪問:
以上提到的解決方案可以幫助大家解決大多數問題,如果需要更完善的規避這些 Audio 的坑可以參考使用這個 JS 插件,使用方法如下:
jdMusic.create([{
src: 'https://jdch5.jd.com/yayoi/res/raw-assets/Sound/A.mp3',
isloop: false,
id: 'demo1',
class: 'demo1',
autoplay: false
}, {
src: 'https://jdch5.jd.com/yayoi/res/raw-assets/Sound/B.mp3',
isloop: false,
id: 'demo2',
class: 'demo2',
autoplay: true
}]);
jdMusic.play(0)
jdMusic.pause()
參考文獻
[1] HTML 5 audio 標簽
http://www.w3school.com.cn/html5/html5_audio.asp
[2] HTML DOM Audio 對象
http://www.w3school.com.cn/jsref/domobjaudio.asp
[3] HTML5 Audio (音頻)
http://www.runoob.com/html/html5-audio.html
[4] audio sprite 工具
http://jsfiddle.net/aarongloege/rQv5h/light/
[5] audioCreate.js
https://github.com/jdf2e/audioCreate.js/tree/master/audiojs