html5中对音频,视频播放原生支持.最近做了一个音乐播放器,得益于快过年了,才能抽出一点时间来总结一下.总的来说,html5对audio的支持非常强大, 难怪flash要死.浏览器上装播放插件的时代已经接近尾声了.目前大多数浏览器都支持了大部分常用的api,ie8及其以前除外(万恶的ie啊).
HTML5 音频虽然很健壮,但有其局限性,这主要取决于它的实现。对于音乐播放器(点唱机播放器)或简单的声音效果,它很有效,但是对于声音密集的应用程序如游戏,它的表现不是很理想。所以有了Web Audio API (Chrome) 和 Audio Data API (Firefox) 无需任何浏览器插件即可进行合成和动态处理音频的能力来帮助人们解决特性缺失的问题.Web Audio与audio标签不同,它给了开发者对音频数据进行处理、分析的能力,例如混音、过滤等,类似于对音频数据进行PS。一般的网站应用应该是用不倒这些API中的,但是一些游戏引擎或者在线音乐编辑等类型的网站应该用得到。请参考文章最后的参考资料.
兼容情况
可见除了ie6,7,8 和opera,其他都支持了.其中安卓在2.1就已经支持了.audio的兼容性还是不错的,当然这只是audio标签.JavaScript接口的支持情况就各不相同了,可能会遇到坑.
API
本来想贴上audio的api,后来发现太多了,就放弃了.有很多都是不常用的.我们这里就讨论几个常用的api和属性.
详情请参考w3c http://www.w3school.com.cn/tags/html_ref_audio_video_dom.asp
总结的过程中也发现audio和video的api是一模一样的.这也方便了广大开发者的学习使用.
重要方法
- 这3个方法是最基础的了,看方法名就知道是干嘛用的了.其中load方法,一般是在改变audio的一些属性后,需要重新加载的情况下使用.
重要属性
- autoplay 设置是否自动播放音频/视频,ios的Safari下不能自动播放,需要和用户交互后才能播放,这是个坑,后面会详细说.
- buffered 会返回一个TimeRanges对象,包含一个起点和终点时间,如果用户拖动音频/视频,则有多个buffer区间,可以通过index访问指定buffer区间.
- currentTime 是非常重要的一个属性了,可以返回当前的播放位置(以秒计),同时也可以设置播放位置,跳跃播放音频.
- played 返回已经播放部分的TimeRanges对象,可以用于做一些计算
- seekable 表示用户当前可以跳跃观看的以缓冲部分区间
- src 音频/视频资源地址
- volume 控制音量
重要事件
- 加载过程的事件流对我们做一些特殊处理是很有帮助的
- canplay 加载ok,可以播放时触发,一般逻辑都从这里开始
- canplaythrough 当全部加载完毕时触发
- 正在下载时触发,配合buffered属性更新加载进度条的逻辑就可以放在这里
- 用户跳跃播放时触发
- 当前播放位置改变时触发,更新播放时间,同步歌词的逻辑都可以放在这里
封装audio
从audio提供的api来说,已经非常强大和完善了.如果是在移动端,我们完全可以尽可能的使用原生api,没必要使用第三方库.我简单封装了一下audio,尽可能的保持原生api的最大灵活性前提下,减少我们的代码量.
js代码
;(function(){ var _init = function(){ var audio = this.audio = document.createElement('audio'); for( var prop in this.audioProp ){ audio[prop] = this.audioProp[prop]; } document.body.appendChild(audio); }; var _bindEvt = function(){ var audio = this.audio, audioEvt = this.audioEvt; for( var func in audioEvt ){ audio.addEventListener(func,audioEvt[func].bind(this),false); } }; var _extend = function(o1,o2){ for(var attr in o2) { o1[attr] = o2[attr]; } return o1; }; /* * 构造函数 */ function Constructor(opton){ opton = _extend({ audioProp : { }, audioEvt : { }, cssSelector : { } },opton); _extend(this,opton); _init.call(this); _bindEvt.call(this); } _extend(Constructor.prototype,{ /* * 播放 * second 指定当前的播放时间 */ play : function(second){ second && (this.audio.currentTime = second); this.audio.play(); }, /* * 暂停 * second 指定当前的播放时间 */ pause : function(second){ second && (this.audio.currentTime = second); this.audio.pause(); }, /* * 时间格式化 * 00:00 */ timeFormat : function timeFormat(number) { var minute = parseInt(number / 60); var second = parseInt(number % 60); minute = minute >= 10 ? minute : "0" + minute; second = second >= 10 ? second : "0" + second; return minute + ":" + second; } }); //兼容amd,cmd,原生js接口 if(typeof module !== 'undefined' && typeof exports === 'object' && define.cmd){ module.exports = Constructor; }else if(typeof define === 'function' && define.amd){ define(function(){ return Constructor; }); }else{ window.APlayer = Constructor; } })();
html代码
var music = new APlayer({
audioProp : {
title : '给我一个理由忘记',
loop : true,
src : "https://ss1.baidu.com/8aQDcnSm2Q5IlBGlnYG/stat/ogg/xinsui.mp3",
},
audioEvt : {
canplay : function(){
},
timeupdate : function(){
$('.currentTime').html(this.timeFormat(this.audio.currentTime));
}
}
});
$(".play").on("touchend",function(){
music.play();
});
$(".pause").on("touchend",function(){
music.pause(5);
});
详情可以看我的github https://github.com/Alan110/desire/tree/master/APlayer
坑爹集锦
编程总不会这么一帆风顺,莫名其妙的问题总是不会少的.下面就罗列一下我遇到的坑吧.
1. 安卓uc浏览器下,对timeupdate事件支持不好,只会很少的触发几次.这是什么概念,就是说我们基本上不能同步当前的播放时间和进度条了,还有歌词.
解决方案:暂时没有
2. 在ios,Safari下,不能自动播放,autoplay,preload属性无效. 或者audio.src='xx' , 加载完后手动调用audio.play()也是不行的.
原因: 在ios,Safari下要求与用户交互后才加载歌曲,播放歌曲,如touchstart , touchend , click等事件.
解决方案 : 在iphone4下touchstart无效, 目测是因为性能不够无法捕捉. 改用touchend就好了
3. ios,Safari同一时间只能播放但音频/视频
解决方案 : 使用 audio sprite 是满足移动 Safari 中多音效需求的最佳解决方案。与 CSS image sprite 类似,audio sprite 可以将所有的音频综合到一个音频流.要注意的是更改 currentTime 并不是百分百正确的。
将 currentTime 设为 6.5,而实际得到的却是 6.7 或 6.2。每个 A sprite 之间需要少量的空间,以避免寻找到另一个 sprite 的尾部。
// audioSprite has already been loaded using a user touch event var audioSprite = document.getElementById('audio'); var spriteData = { meow1: { start: 0, length: 1.1 }, meow2: { start: 1.3, length: 1.1 }, whine: { start: 2.7, length: 0.8 }, purr: { start: 5, length: 5 } }; // play meow2 sprite audioSprite.currentTime = spriteData.meow2.start; audioSprite.play();