移动端视频播放H5踩坑记录(上)

西西吹雪2018-05-25 13:16

前一阵收到了一个云音乐移动H5的制作需求。需求本身不复杂,其中就涉及了广告视频和摇一摇音频两个功能。刚接到需求时一看感觉没什么问题,应该很容易就可以搞定。于是如往常一样,在PC上使用Chrome开发者工具模拟移动设备,开始开发调试。视频内联播放的问题根据网上的给出的方法处理了一下,一切看起来很顺利,三天后整个H5的功能基本上就做好了,自己在iOS上简单自测了下,没发现什么问题,便交付测试了。然而万万没想到,没过多久QA大大就反馈过来各种各样的奇葩问题:UC浏览器上视频退出播放后页面空白出来了,QQ浏览器上视频位置不对啊,safari上没有播放声音啊......于是我开始意识到H5播放播放视频音频的坑,远比我想象的要深。。。于是之后又重新花了一天多的时间,经过各种尝试,把iOS及Android上各种常见浏览器都安装测试了一遍,终于基本解决了各种浏览器下视频和音频播放的兼容问题。

        经过这次教训,我意识到网上的一些解决方案不能盲目跟从,一定要亲身试验过才能下结论。在这里,我把自己遇到的问题和解决方法整理了一下,并综合网上的一些资料,供各位参考,以免继续踩坑和少走一些弯路。

1、固定横屏显示

        为了更好的视频观看体验,本H5采用固定横屏的显示模式。其实现原理想必很多人都做过:判断手机旋转方向或陀螺仪转角,若判断为竖屏则将整个页面进行旋转。这里有个坑需要注意:开启屏幕旋转后,在一些安卓机微信浏览器上,屏幕旋转后重新获取屏幕宽高会有延迟,因些实现上需加一个延时。

$(window).bind('orientationchange', function() {
    setTimeout(function () {    // wx中高宽更新有延迟
        var bodyWidth = document.documentElement.clientWidth;
        var bodyHeight = document.documentElement.clientHeight;
        var orientation = window.orientation;
        if (orientation === 90 || orientation === -90) {
            $body.attr({ style: '' }).removeClass('rotated');
        } else if (orientation === 0 || orientation === 180
            || orientation === undefined && bodyHeight > bodyWidth) {
            isMobile() && $body.css({
                width: bodyHeight,
                height: bodyWidth,
                position: 'absolute',
                left: '50%',
                top: '50%',
                transform: 'translate3d(-50%,-50%,0) rotateZ(90deg)'
            }).addClass('rotated');
        }
        initScreen();
    }, 100);
}).trigger('orientationchange');

2、视频内联播放

        为了更自然的交互体验,传统点击按钮后弹出视频浮层式的播放模式已经过时,越来越多的H5项目要求能够直接在网页上直接播放。给视频节点添加 webkit-playsinline =true和playsinline=true属性可以实现视频在ios微信端及safari上的内联播放,安卓上微信使用了一个X5内核的浏览器,要实现视频的内联播放需要添加x5-video-player-type=h5、x5-video-player-fullscreen=true私有属性,x5-video-orientation用于设置视频内联播放时的显示方向。

function setVideoInline($video, poster) {
    $video
        .attr('playsinline', true)
        .attr('webkit-playsinline', true)
        .attr('x5-video-player-type', 'h5')
        .attr('x5-video-player-fullscreen', true)
        .attr('x5-video-orientation', 'landscape')
        .attr('x-webkit-airplay', true);
}


        本以为这样就万事ok了,但现实是残酷的,你会发现放到QQ、UC、微博、百度等浏览器上,或许是为了增强用户体验,也可能只是为了方便添加自己的广告,它们都会弹出自己内置的视频播放器,进入全屏播放状态,直接无视了设置的内联属性,因此都无法实现内联!其中部分浏览器可手动选择返回内联模式,但是这时会显示视频控制条,可以对video添加pointer-events: none;属性可以将其隐藏。iOS上的QQ浏览器虽然也是X5内核,但可能是低版本的bug,你会发现设置为内联后,视频会被置于最底层,直接从界面上消失了!所以也只能放弃兼容。

        另外,在一些安卓机上,微信浏览器中的视频内联播放后会铺满整个屏幕,因此,当锁定屏幕旋转时,就会和系统底部的系统工具条重叠,这个暂时没有找到好的解决方法。并且在安卓上使用touchend事件会有兼容问题,可能无法触发,需要绑定touchstart事件执行event.preventDefault()。

        因此,在目前各种浏览器厂商各行其道的形势下,要做到所有浏览器的视频内联显然无法实现,只能在限制投放渠道和允许部分浏览器非内联两个方案中二选其一。这就是为什么我们看到的内联全屏播放视频的H5效果都只在微信上投放的原因。

3、视频事件监听

        我们的项目中很可能有这样的需求:视频播放完成后显示某个界面,显示某些按钮或者播放另一段视频,这就需要监听视频的播放状态。于是我们添加一些事件监听器,监听timeupdate、ended、pause等事件。OK,写完代码,测试通过。但是,放到ios的UC浏览器上,你会发现这些事件根本没有触发!这个可能没有很好的解决方案,只能在开始播放时设置一个定时器,若无法监听到timeupdate事件,则直接操作后续操作。

if (isIOS && userAgent.indexOf('UCBrowser') > -1) {
    var hasNoEndedEventTimer = setTimeout(doOnVideoEnded, 3500);
    $video.on('timeupdate.clearTimeout', function () {
        $video.off('timeupdate.clearTimeout');
        clearTimeout(hasNoEndedEventTimer);
    });
}

        微博浏览器也是使用自带播放器播放视频,光监听ended事件还不够,因为若用户在视频全屏播放中途点返回中断播放,触发的是pause事件,这时候需要和视频结束作相同的处理。需要注意的是,当网络不稳定时,视频卡顿也是触发pause事件,需要根据具体业务需求相应处理。

播放体积较大的视频文件,预加载时间可能比较长,play时最好加一个loading界面,并监听timeupdate事件,大于0.1时再移除。测试时发现,若在视频加载好之前执行video.load,会导致在safari、firefox上视频无法播放,需要注意。视频播放完后,若没有其它影响,最好直接移除原来的video元素,以免在某些浏览器上层级太高盖住后续界面。

$video.on('timeupdate.removeLoading', function () {
        if (this.currentTime > 0.1) {
            $('#loading-video').remove();
            $video.off('timeupdate.removeLoading');
        }
    }).on('ended.showShakePage', function () {
        $(this).remove();
    });      


本文来自网易实践者社区,经作者蔡娟授权发布。  


本文未结束,敬请期待下篇。