美文网首页H5HTML5前端开发
Audio在移动端的实践

Audio在移动端的实践

作者: 胖乎乎的萝卜 | 来源:发表于2015-12-01 19:42 被阅读4724次

    好久没写blog了,有三点原因,一是懒,二是懒,三是懒。

    因为最近项目里面有个需求,要在移动端用web的Audio实现音频播放。本想说臣妾做不到啊~然而,还是开始挖坑了。在这里记录下各种坑死人的问题。

    准备

    先看兼容性(下图),可以看到在移动端上用是完全可行的(理论上):

    compatibility.png

    我们再分别看看audio提供的属性,方法和事件

    属性

    params.png

    方法

    way.png

    事件

    event.png

    具体的可以戳这里

    实践

    其实按照上面的方法,随便怎么写怎么玩都可以,但主要有以下几个问题要解决的:

    1.预加载的问题;
    2.加载进度条问题;
    3.多个音频文件切换问题;
    4.其他的兼容性问题。
    

    1.预加载的问题

    我们先来看预加载的流程(如下),先用load去加载音频,当音频可以播放就会触发canplay事件,表示加载已经完成,可以播放,完美。

    patten1.png

    但是,理想和现实总是有区别的,在表现不一的手机上就有问题了。

    问题一:load方法调用了没效果,根本没有加载音频,要调用play方法才开始加载。

    问题二:在三星note3 和锤子T1手机上,有50%的几率预加载失败。如果预加载失败,要切换好几次播放/暂停状态才开始加载播放,或者一直没反应。

    问题三:一般触发load加载音频文件后,音频文件缓冲好会触发canplay事件的。

    在安卓下,触发canplay事件,会有下面问题:

    • 360浏览器audio.seekablefalse;
    • uc浏览器,魅族自带浏览器,微信audio.buffered.length居然为0;

    在iOS下,有以下问题:

    • canplay事件触发后,微信的audio.seekablefalse
    • safariload了之后,canplay事件不触发,点击play后才触发 (9.1版本是正常的);

    看到这里是不是觉得坑大了,想逃?不要急,接着看。

    解决方法

    上面问题总的来说有俩个,一个是加载进度,另外一个就是播放Bug了。这里主要说下问题二的解决方法。

    调用load事件后,对加载进度进行检测,如果直到canplay触发,加载进度一直为0,就判断为预加载失败。然后在点击播放的,设置进度audio.currentTime = 1;,这样就会再次触发加载。这里还有个问题,如果是用zeptotap监听点击播放事件,可以再次加载,但一直不播放,要监听touchend这些事件才行(这个问题纠结N久)。
    这样调整后,在三星note 3 和锤子T1这些有问题的手机上基本没什么问题了。

    2.加载进度条问题

    加载进度,浏览器提供了progress事件,但这个事件会有一些小问题,所以采用setInterval的去实行。正常来说在canplay的时候显示进度条:

    onCanplay: function () {
        this.seekable = this.audio.seekable && this.audio.seekable.length > 0;
    
        if ( this.seekable ) {
            this.timer = setInterval(this.onProgress.bind(this), 500);
        }
    
        var name = this.list[this.index].name || '',
            time = this.list[this.index].time || '';
    
        this.trigger('canplay', time, name, this.list[this.index]);
    },
    
    onProgress: function () {
        if ( this.audio && this.audio.buffered !== null && this.audio.buffered.length ) {
            this.duration = this.audio.duration === Infinity ? null : this.audio.duration;
            this.load_percent = ((this.audio.buffered.end(this.audio.buffered.length - 1) / this.duration) * 100).toFixed(4);
            if (isNaN(this.load_percent)) {
                this.load_percent = 0;
            }
    
            if ( this.load_percent >= 100 ) {
                this.clearLoadProgress();
            }
    
            this.trigger('progress', this.load_percent);
        }
    },
    
    // 对于play触发后才开始加载
    play: function () {
        if (!this.seekable) {
            this.timer = setInterval(this.onProgress.bind(this), 500);
        }
        this.audio.play();
    },
    

    上面代码的逻辑主要是检测audio的buffered,因为不同浏览器对buffered的解析不同,如果跳跃播放,有的会产生多段buffered,所以获取最新的缓存要这样:this.audio.buffered.end(this.audio.buffered.length - 1)

    3.多音频切换问题

    在播放列表里,有多个音频文件,点击可以切换。正常的做法是,用tap绑定点击事件,事件内部这样处理:

    audio.pause();
    audio.setAttribute('src', url);
    audio.play();
    

    在PC的chrome上是很正常的,完美。但是,在手机上就嗝屁了。问题为:偶发性的出现,切换音频后,直接触发音频的ended事件,然后再怎么切换播放/点击都是无效的了。
    这个问题的解决方法很简单,就是在canplay触发的时候再触发play就好,不要切换了音频url马上play

    _t.audioHandler.on('canplay', function (totalTime, name) {
        _t.audioHandler.play();
    });
    

    因为没有预加载的过程,每次都是点击列表的音频才播放,所以这样理论上是可行的。但是如果点击了播放,触发了加载,马上就点暂停,这时候canplay还没触发,会不会有问题?

    4.其他的兼容性问题

    • 关于音频的总时间,理论来说,正常加载的情况,在canplay的时候是可以读取到的,但因为上面一堆load问题,所以音频总时间要手动设置。
    • tab去绑定播放事件好像会有奇葩的问题,用touch系列又太灵敏了,都接受不了可以用fastclick

    暂时还没发生其他问题,下面就看看例子吧。例子分两个,一个是单音频预加载播放,另外一个是多音频列表播放(UI直接用项目的了)。

    例子1:单音频预加载播放

    audio1.gif

    例子2:多音频切换播放

    audio2.gif

    上面俩个例子的代码在这里

    最后

    实践都这里就算完了。不过这里有个更好玩的东西,有兴趣可以看看,非常酷炫。

    在开发的过程中,针对移动端,参考了Audio5js,整理出了个audio的库。代码在这里,有兴趣可以关注下。

    参考:

    相关文章

      网友评论

      本文标题:Audio在移动端的实践

      本文链接:https://www.haomeiwen.com/subject/poijhttx.html