移动端 HTML5 video 视频播放实践
移动端 HTML5 使用原生 <video> 标签播放视频,目前正被广泛的使用,虽然在各个平台上存在各种各样的兼容问题,给开发者带来不小的障碍,不过根据前人的经验,我们还是能从中找到蛛丝马迹来解决在开发中遇到的问题。
此篇文章,主要是总结我在项目中遇到的一些坑,也为后面的小伙伴在使用 video 标签时,提供一些思路和方法。
在移动端售后项目中,我主要遇到两个问题:
- iOS WebView 中异步添加 video 标签无法播放。
- iPhone6s iOS WebView 中异步添加 video 标签需要点击两次才能播放。
一、项目背景
提升网购用户体验,还是要聚焦在售后服务的环节上。所以此次售后体验的飞跃项目(从京东 app -我的-退换/售后进入)对 H5 页面进行了重构,对业务功能进行了升级。
其中一个升级模块就是在申请售后环节,在之前仅有拍照基础上,增加了音频和 小视频的功能,借此机会为用户提供更丰富的多媒体反馈功能。技术上采用的是 H5 + JDReact 结合的模式。用户在点击录视频 icon 会触发调用React原生应用录制小视频,后端同学采用轮询方式往 H5 页面动态添加 video 标签。之后用户可以马上点击刚刚录制的小视频播放观看。算是在申请时候为用户增加的一个更为直观反馈的功能,也便于京东客服的处理。
二、了解 video 标签一些基本的属性
既然决定用 video 标签实现视频回显播放,那么先了解 video 标签都有哪些重要的属性。
属性名字 | 描述 |
---|---|
src | 视频媒体地址 |
autoplay | 自动播放。iOS 蜂窝网络和 safari 中不支持。WebView 中可单独配置开启。 安卓可能支持该属性 |
loop | 循环播放。iOS 支持,Android 可能支持 |
controls | 播放器控制条。iOS 和 Android 支持 |
width | 视频的宽。iOS 和 Android 都需要给出 |
height | 视频的高。iOS 和 Android 都需要给出 |
poster | 封面图片。iOS 支持,Android 不一定支持,而且各产商表现差异大 |
对于 width 和 height,比较流行的做法还是以 1px * 1px 的形式放在视觉边缘的位置。以此避免隐藏和设置 0 宽高的 video,使得视频处于未激活状态。另外 poster 封面的在各个平台的兼容性并不好,体验较差。所以我直接用 <img /> 来做一个初始化展示的效果图。
<div class="video-wrap"> <img src="../media/demo.jpg"> <video id="video" height="2" width="1" src="../videoDemo.mp4;"></video> <span class="play"></span> </div>
这里有一个小技巧,对于宽高。初始设定,可以使用 1px * 2px。对于安卓手机全屏播放基本会初始竖屏播放,而不是先横屏播放,然后加载视频后得到视频宽高比后再切换成竖屏播放。
三、了解 video 标签一些基本的方法
在打算使用 video 标签时,我专门去网上找了一些资料。这样可以在遇到兼容性的时候做到有备无患。
方法 | iOS | Android |
---|---|---|
play | 只是要播放视频,响应的是 video.play() 方法,并不代表已经开始播放 | 和 iOS 一样,仅是响应 video.play() 方法 |
durationchange | 会执行一次,一定会获取到视频的 duration | 可能会执行多次,只有最后一次才能获取到真实的 duration,前面的 duration 都是0;但低版本 Android 可能获取到的 duration 是 0 或 1 |
canplay | 可以认为是视频元素没有问题,可以运行,没有更多含义了,基本用不上 | 同 iOS |
canplaythrough | 会有明确的缓冲,表示可以流畅播放了 | 没有什么用,视频仍然会卡住,数据可能还没有开始加载 |
playing | 明确表示播放开始了 | 依然没有用,视频可能并没有开始播放 |
progress | 有明确的下载,可以获取到当前的 buffer,并且全部下载完毕后不在触发 | 不一定有明确的数据下载,并且全部下载完毕后依然继续触发 |
timeupdate | 会有明确的进度变化,可以获取到currentTime | 进度不一定变化,currentTime 可能总是 0,但是第一次有 currentTime 变化的timeupdate 事件一定代表了视频开始播放了 |
error | iOS 中会有明确的错误抛出 | Android 中某些浏览器会莫名其妙的抛出 error |
stalled | 网络状况不佳,导致视频下载中断 | 在没有 play 之前,也可能会抛出该事件 |
虽然 video 的方法有很多,但是兼容性还是比较头疼的。不过在项目我仅仅用到了 play,load 方法。下面说一下我遇到的问题吧。
iOS WebView 中异步添加 video 标签无法播放。
在售后的项目中,因为需要支持 JDReact 原生应用提供多媒体功能。所以小视频只需要在京东 app 内 WebView 做适配就可以。第一个问题就出现了。后端将视频 video 标签动态添加到页面中后,iOS app 内点击播放。发现无法播放。而在安卓版的京东 app 内 x5 内核的浏览器是可以播放的。
mediaWrapper.delegate('.load-video .video-wrap','click',function(){ video.play(); video.addEventListener('ended',function(){ video.webkitExitFullscreen(); }); });
当然有小伙伴会表示,那不动态添加 video 标签,采用更换 src 的方式来回显用户录制的小视频。经过测试,依然无法播放。究其原因,video 标签和audio 标签不同于一般的 img 多媒体标签,更换 DOM 或者属性值就能使其激活生效。video 标签和 audio 标签本身具有了时间特性,因而采用了不同的模式。所以在播放视频之前,需要调用 video 的 load 方法来重新加载更新视频。
mediaWrapper.delegate('.load-video .video-wrap','click',function(){ if(videoIsPlay){ video.play(); return false; } video.load();//PAY ATTENTION! video.play(); video.addEventListener('ended',function(){ video.webkitExitFullscreen(); videoIsPlay = true; }); });
iPhone7 的京东 app 可以播放视频了。感觉革命已经成功。突然发现自己的 iPhone6s 依然不能播放,再点一次突然又可以播放了。这又是什么原因?
iPhone6s iOS WebView 中异步添加 video 标签需要点击两次才能播放
我们先看一下需要点两次效果图。
京东 app 采用的内嵌 UIWebView 内核浏览器,如果比较古老的话,会触发需要用户点两次 bug。load 方法和 play 方法在 iOS 上都是需要用户手动触发的。就算你在点击回调里写两次 load 方法或者 play 方法依然是没用的。不过可以有效利用移动端 click 事件的 300ms 延迟特点来解决两次点击播放问题。给 video 同时绑定两个事件 tap 和 click 事件,在 tap 事件和 click 都调用 load 方法。并且在 click 事件中调用 play 方法。这样就能完美解决该问题了。
mediaWrapper.delegate('.load-video .video-wrap','tap',function(){ var video = document.getElementById('my-video'); if(videoIsPlay){ return false; } video.load(); });
看一下修改后的效果图:
四、最后总结
我的移动端的经验还不是很丰富,对于 video 标签,只能说里面水太深~。不过赶在618之前圆满的完成了售后客服的飞跃项目,获得不少成长,感谢并肩战斗的后端、设计、产品等同学,特别感谢封面设计:竹子。最后我们来整体看一下售后小视频的功能吧。
参考资料
HTML5 Video Events and API
https://www.w3.org/2010/05/video/mediaevents.html
Exploring HTML 5’s Audio/Video Multimedia Support
http://www.devx.com/webdev/Article/43324
移动端 HTML5 <video> 视频播放优化实践