chimee 移动端插件开发

chimee 对外正式开源后,就有小伙伴开始咨询,chimee 什么时候可以支持移动端。在 PC 端较为稳定之后,我们就开始着手做移动端的开发了。

开发之前首先要考虑一个事情,chimee 在移动端能够支持哪些格式:

chimee 在 PC 端主要支持三种格式的视频播放: MP4、HLS、Flv。PC 浏览器本身并不支持 HLS 和 Flv 的播放(sarifi 支持 HLS)。我们通过 Media Source Extensions 实现对这两种格式视频的编解码,达到播放的目的。

但是看看 MSE(Media Source Extensions) 的兼容性:

移动端播放器插件开发 - 图1

MSE 在移动端的兼容性太差了,移动端主流浏览器基本都不支持这个 api,好在移动端支持 HLS 比较好, Flv 不论 PC 还是移动端都不支持

移动端播放器插件开发 - 图2

那么在移动端,chimee 能够播放 MP4, HLS,不需要任何编解码器,直接使用原生的就好了,参考如下配置:

  1. new Chimee({
  2. // ...
  3. box: 'native'
  4. })

播放格式搞定!现在开始跟大家分享我在移动端插件开发中遇到的坑。

思考: 需要哪些插件,是否可以直接使用已有的插件?

已有的插件分为两种:

  • 展示类插件
  • 操作类插件 展示类插件只要略微修改样式即可在移动端直接使用。

操作类插件都是基于 mouse 事件来实现,如果直接在移动端使用,会出现以下问题:

  1. click 300ms 延迟问题。
  • 问题来源是,当用户第一次触发后,浏览器会等 300ms 来确定用户是否是双击事件,以此来触发手势
  • 解决问题:简单点处理的话是, 判断 touchstart ,touchend 这个事件,如果在这俩事件中, 位移足够小,时间也短的时候,认为是一次 tap (下面会在详细介绍这些判断规则)
  1. mousemove 只会在手指移开屏幕的时候触发,而不是在滑动中触发。
  • 问题来源可以看下官方文档的解释:

移动端播放器插件开发 - 图3

  • 解决方案的话,考虑替换为 touchmove 事件。
  1. 没能触发 mouseenter、 mouseleave 等事件,目前也没有 touchenter、touchleave 事件可以用,解决方案是,将其替换为其他事件,或者删除对这些事件的监听。

替换为 touch 事件后,又暴露了新的问题:

  1. 透传
  • 问题来源: 比如有一个对话框,点击提交按钮后隐藏对话框,当用户点击提交后,对话框隐藏了,然后 300 ms 后 click 还会触发, 此时 click 事件对象的 target 将不是那个提交按钮, 可能会触发当前点所在元素的 click 操作, 或者 input 的 focus 事件。
  • 解决方案:
    • 遮挡, 在隐藏对话框后,设置一个 300 ms 的透明遮罩,阻止事件透传下去。
    • pointer-events,隐藏对话框时,给对话框底部元素设置 pointer-events: none ,300ms 后再设置为 auto。2. 播放器手势很可能会触发浏览器的一些默认手势, 可以 e.preventDefault() 来阻止事件冒泡,达到阻止默认事件触发的目的。 思考这么多,终于开始写插件了。在移动端往往要判断用户进行了什么手势操作,而这些操作得通过 touchstart, touchmove, touchend, touchcancel 这四个事件的来一起判断。

可以看下从 hammer.js 学习来的一些手势规则

  1. // tap
  2. TapRecognizer.prototype.defaults = {
  3. event: 'tap',
  4. pointers: 1, // 触点
  5. interval: 300, // 双击操作的最小间隔时间
  6. time: 250, // 手指在屏幕上的最长时间
  7. threshold: 9 // 最大移动距离
  8. };

总结: tap 操作规则:

  • 单个触点
  • 手指在屏幕的最长时间为 250 ms
  • 两次点击屏幕的最小间隔为 300 ms
  • 最大距离为 9
  1. // press
  2. PressRecognizer.prototype.defaults = {
  3. event: 'press',
  4. pointers: 1,
  5. time: 251, // 手指在屏幕上的最短时间
  6. threshold: 9 // 最大移动距离
  7. };

总结: press 操作规则:

  • 单个触点

  • 手指在屏幕的最短时间为 251 ms

  • 最大距离为 9

  1. // pan
  2. PanRecognizer.prototype.defaults = {
  3. event: 'pan',
  4. pointers: 1,
  5. threshold: 10// 最短移动距离
  6. };

总结: pan 操作规则:

  • 单个触点

  • 最短距离为 10

  1. // swipe
  2. SwipeRecognizer.prototype.defaults = {
  3. event: 'swipe',
  4. pointers: 1
  5. threshold: 10, // 最大移动距离
  6. velocity: 0.3 // 最小速度
  7. };

总结: swipe 操作规则:

  • 单个触点

  • 最短距离为 10

  • 最小速度是 0.3 对于插件,我们可以引用 hammer.js 或者 AlloyFinger 手势库,在 create 方法里对 video,container, warper,以及插件自身 dom 进行事件代理。这样书写的话, 会把 create 方法写的比较乱。我们有更好的方式:对 chimee 的 events 对象进行一层处扩展,将事件全部放在 events 中。

(说了这么多, 最想说这一句)为了更好的服务开发者,我们决定提供一个中间插件来暴露这些手势操作出来,duang~~~ chimee-plugin-gesture 横空出世了~~, 这个插件只是将播放器需要的手势就进行了封装,体积也会比其他手势库小很多。

具体使用方式,查看 readme.md

目前提供的手势操作有 tap、press、panstart、panmove、panend、swipe

手势操作问题处理好了,就可以开发上层逻辑了。

将 PC 端的控制条迁移到移动端:1. click 替换为 tap 事件2. pan 替换进度条滑动操作3. 删除使用率不高的 volume 组件

内联播放和同层播放的问题

先介绍几个概念:

内联播放:通过设置 playslinewebkit-playsline 这两个属性达到在页面内播放的目的,这样在点击播放的时候不会自动去全屏播放。

同层播放:微信提出的概念,主要解决安卓端微信视频播放器的高层级和全屏问题,可以先看下同层播放的一些 API,再谈谈我的理解及可以做哪些事情?

  1. x5-video-player-type="h5" 启用 h5 同层播放器
  • 值是 h5,其他值没有效果
  • 需要在播放前就设置好
  1. x5-video-player-fullscreen="true" 启用全屏
  • 主要作用: (文档描述)如果不申明此属性,页面得到视口区域为原始视口大小(视频未播放前),比如在微信里,会有一个常驻的标题栏,如果不声明此属性,这个标题栏高度不会给页面,播放时会平均分为两块(上下黑块)

  • 值是 Boolean 选择是否进入全屏

  • 同样需要在播放前设置好

  1. x5-video-orientation 控制横竖屏
  • 值: landscape 横屏, portraint竖屏,landscape | portraint根据屏幕自动横竖屏
  1. 事件
  • x5videoenterfullscreen 进入全屏回调
  • x5videoexitfullscreen 退出全屏回调 以上是微信的一些概念,我在魅族 pro 6s的微信中进行了一些测试。

我的结论是,确实解决了高层级的问题,video 层上可以放一些组件了。但在播放的时候还是默认全屏的,不论我是否设置 x5-video-player-fullscreen="true",按照微信这个解决方案,其实还是没有解决所有问题,不过对于全屏带来的问题,也有几种不同的解决方案:

  1. 不使用自己的播放器 UI,而是使用系统默认的 UI, 就是将微信的 H5 同层播放去掉,这样可以在页面内正常播放,但是这样就不可以在 video 元素上放置组件。

  2. 特定布局下, 比如:

  • 移动端播放器插件开发 - 图4

  • 实现方式:

    • 点击播放就会触发全屏事件,监听 x5videoenterfullscreen 事件进行下面一系列操作

    • 进入全屏后,需要将视频铺满全屏

  1. window.onresize = function(){
  2. video.style.width = window.innerWidth + "px";
  3. video.style.height = window.innerHeight + "px";
  4. }
  • 更新部分样式:
  1. #wrap{
  2. z-index: -1; // 这样覆盖文档流后的东西显示出来
  3. }
  4. container, video {
  5. background: #fff;
  6. }
  7. video{
  8. objcet-position: 0 0; // 将视频放在顶部
  9. }
  • 退出全屏时,关闭窗口
  1. video.addEventListener("x5videoexitfullscreen", function(){
  2. WeixinJSBridge.invoke('closeWindow')
  3. })
  • 只要将 video 的样式稍微变化就可以实现另一种布局方式:
  1. video{
  2. objcet-fit: fill; // 铺满全屏
  3. }

移动端播放器插件开发 - 图5

其他坑

移动端的坑还是比较多的:

  1. 坑一: autoplay 属性
  • 问题描述:设置 autoplay: true 视频依然无法自动播放,在 iOS 的更早的版本之前,meta 信息也不可以预加载,这是为了避免给用户造成高额流量费用而作出的妥协,顺便还能节省用户手机电量。默认触发 play 没有效果,只有在用户有手势操作才可以触发播放。

  • 当前解决方案:

    • 没有音轨的视频

    • <video muted>

    • 可视区域内

满足这三个条件的视频可以在 iOS 10+ safari 中自动播放,注意一旦获得音轨之后或者不在可视区域内均会暂停播放

  • 注意: 这些只针对与 safari, 针对第三方 app

wkwebview: mediaTypesRequiringUserActionForPlayback, allowsInlineMediaPlayback uiwebview:mediaPlaybackRequiresUserAction, allowsInlineMediaPlayback

可以设置这俩属性来定义用户的 autoplay | playsinline 是否生效

  1. 坑二: preload
  • 问题描述: iOS 10+ 微信中, 没有加载视频 meta 信息,设置 preload 无效

  • 解决方案:

  1. document.addEventListener("WeixinJSBridgeReady", function () {
  2. player.load();
  3. // 咦, 既然可以 load 那是不是可以 autoplay(不论有无音轨, 当然是可以的
  4. player.play();
  5. }, false);
  • 坑中有坑:(其实是我没有注意到的小细节…)

当我实例化播放器的过程是一个异步操作的话, 可能 ready 回调中,player 还未定义。而且,这个 play 或者 load 只能在 weixinJSBridge 的事件回调中执行,可以用下面代码达到目的

  1. WeixinJSBridge.invoke('getNetworkType', {}, function (e) {
  2. video.play();
  3. }, false);
  1. 小问题:
  • iOS 下会有一个大的播放按钮
  1. video::-webkit-media-controls-start-playback-button {
  2. display: none;
  3. }
  • iOS 在点击某元素的时候点击的时候,会有一层灰色的遮罩
  1. #wraper{
  2. -webkit-tap-highlight-color:rgba(255,255,255,0)
  3. }
  • iOS 音量只可以通过物理按键来触发,直接设置 volume 无效
  1. 还有一些已知问题,还没有答案的那种(有人知道答案可以分享下)
  • uc / 360 / qq 浏览器还是会,还是会调用自身浏览器的播放器,播放完成之后,还会有一个小广告(惊喜不?)

如何调试移动端

如何调试,有几个比较好的工具推荐

  1. vconsole, 不用 USB 连接,引入一个 js 文件就可以获得控制微型控制台,可以看到 log / 网络请求 / html 结构等,就是输入代码的时候太麻烦了一些 2. TBS Studio 用来调试安卓下的微信3. 使用 Mac safari 调 iOS safari。 使用方式4. chrome 调 chrome 内核浏览器。 使用方式

最后

chimee 移动端可以用了,欢迎大家使用和提 issue。

参考

H5同层播放器接入规范

Muted Autoplay on Mobile: Say Goodbye to Canvas Hacks and Animated GIFs!New Policies for iOS

hammer.js

iOS 10 Safari 视频播放新政策

Touch Events