本人在双十一期间,做的一个移动端互动项目中,遇到一个在 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/)。
示例代码:
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 标签,但是这个情况下要注意,各浏览器是是支持在同一页面播放多个音频的,而项目场景基本只允许播放一个音频,这个得注意控制播放当前音乐时要停止其他音乐项的播放。
示例代码:
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 标签给我们提供了一些额外的信息来指定播放哪一时间段,方法是在媒体文件后面跟随(“#”)格式
// 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