2

在座的傻 x

本喵来了

今天写的文章一(亿)级棒!

没错,今天要说的对象就是“网页播放器”这个逼,这玩意看起来没什么难度,但实际上有几个坑,下面由本帅气无敌喵逐一解说如何解决在开发中可能会遇到的坑。

  1. 无法自动播放
  2. 全屏问题
  3. 手机端播放器自定义控制条无法显示

01无法自动播放

不就是 video 标签添加一个 autoplay 吗?很遗憾...事情并没有这么简单。

桌面版的 Safari 在 2017 年的 11 版开始禁止了带有声音的视频自动播放,2018 年 4 月份发布的 Chrome 66 也禁止了,随后各个浏览器都跟随着苹果和谷歌两位大佬的脚本逐一禁止了。

请注意,是禁止带有声音的网页视频自动播放,因此,自动播放和声音是有奸情!!!

想要自动播放可以通过以下几种方式解决。

  • video 标签添加一个 muted 属性(静音)

静音的视频是允许自动播放。

  • 用户在网站上进行了互动

    这个办法在字面上可能很难理解,举个栗子吧。

杰瑞鼠打开了浏览器,访问了黄网“TOM and JERRY”的首页(带有视频),在首页点击某个黄涩视频链接,进入到了这个视频详情。

那么假设首页和这个视频详情的 video 都有 autoplay 这个属性,没有 muted 属性,请问这两个视频是否在网页加载的那一刻起能够自动播放吗?

答案是:首页的 video 不能自动播放,但是视频详情里的 video 可以播放(且有声音),因为在同一个会话里,在进入视频详情前,用户已经打开了同站点下的首页,也就是说,用户已经与网站进行了交互,而用户刚进入首页前,是没有访问过同站点下的其他网页,也即是没有任何交互,因此首页的 video 不能自动播放。

  • 浏览器设置“允许自动播放”

这种方法就是全面开放自动播放功能,没有“静音”和“交互”的限制了,需要用户自行设置。以 chome 浏览器为例,在地址栏访问 chrome://flags/,搜索“Autoplay”,即可搜索到该设置(不过,高版本的 chrome 已经不是在这里设置了,本喵也找不到设置的地方)。

    再以火狐浏览器为例,在选项里找“自动播放”,即可找到。

  • 浏览器域名白名单(未证实)

听说浏览器有个域名白名单的东西,只要将域名填入该白名单,访问相关网站就可以自动播放,但目前为止本喵手上没有足够证据证实各个大厂的 py 交易。

上述几种自动播放解决方法各有缺点,为了让用户体验好一点,有时候,我们需要检测网页打开时是否支持自动播放,若不支持,则在视频中间显示播放按钮或者出现其他的样式,下面贴一下检测自动播放代码。

var promise = document.querySelector('video').play()
if (promise !== undefined) {
  promise.then(() => {
    // 支持自动播放且有声音
  }).catch(error => {
    // 不支持自动播放且有声音,这个时候可以在视频上面显示一个播放图标,提示用户自己点击播放
  })
}

    但不是所有的浏览器的 video.play() 都支持返回 promise。想了解更多,可以到这个网站看看。

https://developer.mozilla.org...

02 全屏问题

废话不说,先贴代码。

<!-- 这里专门用一个 div 包含 video,这样做是有原因的,下面有说明 -->
  <div class="video-box">
    <video src="xxx" poster=""></video>
  </div>
  ​
  <script>
    // 检测是否全屏(IOS 端无效)
    function checkFullScreen() {
      var isFull = document.webkitIsFullScreen || document.mozFullScreen ||
        document.msFullscreenElement || document.fullscreenElement
      if (isFull == null || isFull == undefined) {
        isFull = false
      }
      return isFull
    }

    // 全屏
    function toggleFullScreen() {
      var videoBox = document.querySelector('.video-box')
      // video 是 video-player 里面的 video 标签,这个用于 ios 端全屏
      var video = document.querySelector('.video-box video')
      var isFull = checkFullScreen()
      //W3C
      if (videoBox.requestFullscreen) {
        isFull ? document.exitFullscreen() : videoBox.requestFullscreen()
      }
      //FireFox
      else if (videoBox.mozRequestFullScreen) {
        isFull ? document.mozCancelFullScreen() : videoBox.mozRequestFullScreen()
      }
      //Chrome
      else if (videoBox.webkitRequestFullScreen) {
        isFull ? document.webkitCancelFullScreen() : videoBox.webkitRequestFullScreen()
      }
      //IE11
      else if (videoBox.msRequestFullscreen) {
        isFull ? document.msExitFullscreen() : videoBox.msRequestFullscreen()
      }
      // IOS(特例)
      else if (video.webkitEnterFullscreen) {
        video.webkitEnterFullscreen()
      }

      // 这里可以做一些判断,更换缩放图标(对 IOS 端没效果)
      if (isFull) {

      } else {

      }
    }
  </script>

    上面的全屏代码适用于 pc(Windows和Mac)、移动端(安卓和IOS),主要使用到了 requestFullscreen API,该 API 适用于 PC 和安卓,但很遗憾,IOS 不适用,IOS 端就是个另类,只能用 webkitEnterFullscreen API 进入全屏,而且没有退出全屏的 API,进入全屏后,只能用 iphone 的手势或自带控件进行退出全屏。

如果你发现 requestFullscreen API 失效,以下网站有说明 requestFullscreen API 失效的原因。

https://stackoverflow.com/que...

But 事情没有这么简单,在开发过程中依旧会遇到很多问题,下面逐一说明。

  • PC 端全屏后无法监听 ESC 键

在 PC 端里,有时候需要退出全屏后做一些操作,点击退出全屏按钮跑逻辑是没问题的,但是不要忘记,按 ESC 键也是退出全屏操作,也需要进行跑逻辑。而当元素使用了 requestFullscreen API 进行全屏时,不需要写任何代码,ESC 键就会默认用来退出全屏,我们自然会想到用 onkeydown 事件来监听 ESC,虽然 onkeydown 事件是可监听,但是唯独 ESC 键是无法监听。

document.onkeydown = function () {
    if (event.keyCode == 27) {
    // 退出全屏
    }
}

    也即是说,上面的代码,按 ESC 是不走的。那咋办?别担心,我们可以利用 onresize 曲线救国,当用户按下 ESC 时,全屏就会失效,windows 的宽高会变小,即会触发 onresize,这个时候再检测是不是非全屏情况,再跑逻辑。

window.onresize = function () {
    if (!checkFullScreen()) {
    // 按 esc 退出全屏需要执行的代码
    }
}
  • IOS端全屏无法自定义控制条

由于 IOS 端的全屏只能调用 webkitEnterFullscreen 将 video 标签进行全屏(其实就是 IOS 端内置的全屏,注意:是只能针对 video 标签全屏),全屏时是不能显示自定义的控制条。

But 我们可以用样式写一个“假全屏”的代码。

<script>
    // 为了方便,下面用了 jq
    // 判断是否是 IOS 端
    var isIOS = !(navigator.userAgent.indexOf("Android") > -1) && (/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent))

    function toggleFullscreen() {
      if ($('.video-box').hasClass('fullscreen')) {
        // 这里设死高度 210 了,可以自己更改
        $('.video-box').removeClass('fullscreen').removeClass('transform').width('100%').height(210).css('margin', '0')
      } else {
        $('.video-box').addClass('fullscreen')
        if ($(window).width() < $(window).height() && isIOS) {
          $('.video-box').width($(window).height()).height($(window).width()).css('margin', '-' + $(window).width() / 2 + 'px 0 0 -' + $(window).height() / 2 + 'px').addClass('transform')
        } else {
          $('.video-box').width($(window).width()).height($(window).height()).css('margin', '-' + $(window).height() / 2 + 'px 0 0 -' + $(window).width() / 2 + 'px')
        }
      }
    }
    // 重力感应,改变屏幕方向,如果是全屏时,也进行相应的改变
    window.onresize = function () {
      if ($('.video-box').hasClass('fullscreen')) {
        if ($(window).width() < $(window).height() && isIOS) {
          $('.video-box').width($(window).height()).height($(window).width()).css('margin', '-' + $(window).width() / 2 + 'px 0 0 -' + $(window).height() / 2 + 'px').addClass('transform')
        } else {
          $('.video-box').width($(window).width()).height($(window).height()).css('margin', '-' + $(window).height() / 2 + 'px 0 0 -' + $(window).width() / 2 + 'px').removeClass('transform')
        }
      }
    }
  </script>
  ​
  <style>
    /* 屏幕旋转的样式 */
    .video-box.fullScreen.transform {
      transform: rotate(90deg);
      -ms-transform: rotate(90deg);
      -webkit-transform: rotate(90deg);
      -o-transform: rotate(90deg);
      -moz-transform: rotate(90deg);
      transform-origin: 50%;
      -ms-transform-origin: 50%;
      -webkit-transform-origin: 50%;
      -moz-transform-origin: 50%;
      -o-transform-origin: 50%;
    }

    ​

    /* 屏幕改变方向时,播放器控制条,需要固定 */
    .video-box.fullScreen.transform .control {
      position: fixed;
    }

    ​

    /* 全屏需要更改位置 */
    .video-box.fullScreen {
      position: fixed;
      top: 50%;
      left: 50%;
      z-index: 10000;
    }
  </style>

上面的代码也兼容安卓端

    不过这种方式存在一些体验问题,比如:使用重力感应横屏时,浏览器的地址栏会浮在上面,这个地址栏是无法去掉的。

  • 视频全屏时也需要将控制条进行全屏

估计没有傻 x 产品会用播放器自带的丑陋控制条吧......既然我们要自定义控制条的样式,那就不会在 video 标签添加 controls 属性,而是在 video-box 的 div 下添加一个控制条相关的 div。

<div class="video-box">
 <video src="xxx" poster=""></video>
 <!-- 控制条 -->
 <div class="ctrl-box"></div>
</div>

    那么全屏时,我们不应该只考虑将 video 进行全屏,同时也要将控制条进行全屏,实现很简单,只要将 video-box 这个父类标签进行全屏即可。这也是为什么全屏代码里是将 video-box 进行全屏,而不是单独将 video 全屏,以及为什么要有个父类 div 包住 video。

  • iframe 里面的元素全屏失败

如果 video 标签是在 iframe 里,iframe 必须声明允许全屏。

<iframe src="" allowfullscreen="allowfullscreen" mozallowfullscreen="mozallowfullscreen" msallowfullscreen="msallowfullscreen" oallowfullscreen="oallowfullscreen" webkitallowfullscreen="webkitallowfullscreen"></iframe>

03 手机端播放器自定义控制条无法显示

如今,手机端主要分为两大阵容:安卓和 IOS,在这两个端里,我们自定义的播放器控制条都会出现各种问题,下面一一列举。

  • 安卓

在安卓端里,因为比较开放,浏览器可以说是百花齐放,有各种各样的功能,其中,有个对开发者不友好的情况就是某些浏览器(例如:QQ 浏览器)会去掉我们自定义的播放器控制条,改用浏览器内置的默认控制条。

解决方法如下:

...

...

...

无解!!!

先等等,别急着抓本喵猫尾。这个确实是无解的,有些浏览器厂商就是不愿意开发者可以自定义控制条,你可能会说为什么像虎牙直播这种大厂在手机上的 QQ 浏览器可以使用自定义控制条,那是因为......嗯......他们有 py 交易,没错,就是 py 交易!

例如:QQ 浏览器就有一个域名白名单的东西,而虎牙直播的域名就在这个白名单里,所以我们访问虎牙直播网站时,可以发现虎牙直播是可以显示自定义控制条。

不信?你试下在电脑使用 Fiddler 软件在本地做下代理,让手机和电脑连接同一个局域网,配置这个代理,然后再在电脑上做一下 hosts,将虎牙的域名映射到本地 IP,你就可以在手机里的 QQ 浏览器通过虎牙的域名访问到自己的网站,看看是不是自定义控制条显示出来了。

有关这个问题,本喵还特地在思否提问了。

https://segmentfault.com/q/10...

没错,就是无解,所以哪个傻 x 产品问你为什么这个功能无法实现,你就回答他,“你在教我做事?”

  • IOS

大家都清楚 IOS 系统是封闭的,封闭到规定 IOS 端里的所有厂商的浏览器都不能支持 requestFullscreen API,只能支持独有的 webkitEnterFullscreen API,全屏时只能显示苹果的那一套控件,导致全屏是没任何办法显示自定义控制条的,不过在非全屏情况下,是可以显示自定义进度条,当然你也可以用上文那里的“假全屏”代码。

好了好了

这期干货就到这里了

本喵要捉杰瑞鼠了

溜了溜了


技术四毛喵
26 声望0 粉丝

喜欢搞机,喜欢写bug,喜欢炒老板鱿鱼......