h5开发|仿App功能:视频进入视口自动播放

1,293 阅读2分钟

1. 需求背景

前段时间接到一个需求,运营希望该h5页面能够实现视频进入视野自动播放,就像下面这样:

1619529786897469 (1).gif

这能忍,不能忍又咋整,谁让她是需求方呢。因为video坑点还是蛮多的,很多webview不支持自动播放,安卓设备种类那么多,即便用hack的方法实现了,也不能保证所有设备都兼容。

最后,采用了折中的方案:端内支持video进入视口自动播放(因为两端webview都支持自动播放),端外可以不支持。

2. 实现

要实现该功能,最方便的方法当然是使用IntersectionObserver啦。先看一下该API的兼容性:

image.png

兼容还不是很好,生产环境下还是得引入polyfillgithub.com/w3c/Interse…

IntersectionObserver

IntersectionObserver可用于判断某个元素是否进入了"视口"。所以可以在元素进入到视口以后执行我们的回调函数。

对于该API不熟悉的可以移步IntersectionObserver API 使用教程-阮一峰先学习一下。

思路

  • 首先需要把每一个模块都监听起来;
  • 当模块完全进入视口以后就让该模块内的video播放。

whiteboardappdotorg20210505140636.png

开干

demohtmlcss代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      padding: 0;
      margin: 0;
      box-sizing: border-box;
    }

    html,
    body {
      font-size: 100px;
      padding: .1rem;
      overflow-x: hidden;
      background: linear-gradient(thistle, wheat);
    }

    .title {
      font-size: .22rem;
      margin: 50px auto;
      color: seagreen;
      text-align: center;
      border: .02rem dashed sienna;
    }

    .video-box {
      width: 100%;
      height: 4rem;
      border: .04rem solid;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      margin-bottom: .5rem;
    }

    .one {
      border-color: seagreen;
    }

    .two {
      border-color: skyblue;
    }

    .three {
      border-color: thistle;
    }

    .four {
      border-color: tomato;
    }

    .five {
      border-color: rosybrown;
    }

    .video-box>p {
      font-size: .2rem;
      color: sienna;
      margin-bottom: .2rem;
    }

    video {
      width: 2.8rem;
      height: 1.8rem;
      border-radius: .1rem;
      object-fit: cover;
    }
  </style>

</head>

<body>
  <h2 class="title">h5视频进入视口自动播放Demo</h2>
  <section class="video-box one">
    <p>模块一</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
  <section class="video-box two">
    <p>模块二</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
  <section class="video-box three">
    <p>模块三</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
  <section class="video-box four">
    <p>模块四</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
  <section class="video-box five">
    <p>模块五</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
</body>
</html> 

为了模拟出现在视口自动播放的效果,页面总共有五个视频模块:

image.png

下面就需要补上js,完成画龙点睛之笔:

  • 监听所有的模块,即video-box类:
  function arrayLikeToArray(arrayLike) {
    return Array.from(arrayLike);
  };

  const sectionList = arrayLikeToArray(document.querySelectorAll('.video-box'));
  
  // 监听所有的模块Dom
  const elementsObserve = new IntersectionObserver(entries => {
    entries.forEach(item => {
      // 如果模块出现在视口内
      if (item.isIntersecting) {
        const video = item.target.getElementsByTagName('video')[0];
        video.play();
      }
    })
  }, {
    // 此处表示当前被监听元素完全出现在视口时再触发上面的回调:即播放模块内的视频
    threshold: [1],
  });
  sectionList.forEach(item => {
    elementsObserve.observe(item);
  });

完成上面的代码,视频就能够实现出现在视口后自动播放,但是上面的代码可能会(不同浏览器/webviewvideo同时播放时限制不一样)出现页面多个视频在同时播放。

  • 每次只允许一个视频在播放
  // +
  const videoList = arrayLikeToArray(document.querySelectorAll('video'));
  function onlyOneVideoCanPlay(video) {
    videoList.forEach(item => {
      if (item === video) {
        item.play();
      } else {
        item.pause();
      }
    })
  }
  
  function arrayLikeToArray(arrayLike) {
    return Array.from(arrayLike);
  };

  const sectionList = arrayLikeToArray(document.querySelectorAll('.video-box'));
  const elementsObserve = new IntersectionObserver(entries => {
    entries.forEach(item => {
      if (item.isIntersecting) {
        const video = item.target.getElementsByTagName('video')[0];
        // -
        onlyOneVideoCanPlay(video);
      }
    })
  }, {
    threshold: [1],
  });
  sectionList.forEach(item => {
    elementsObserve.observe(item);
  });

效果:

chrome下需要先手动点击播放一个视频后再滑动。

屏幕录制2021-05-07 下午9.01.38 (1).gif

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      padding: 0;
      margin: 0;
      box-sizing: border-box;
    }

    html,
    body {
      font-size: 100px;
      padding: .1rem;
      overflow-x: hidden;
      background: linear-gradient(thistle, wheat);
    }

    .title {
      font-size: .22rem;
      margin: 50px auto;
      color: seagreen;
      text-align: center;
      border: .02rem dashed sienna;
    }

    .video-box {
      width: 100%;
      height: 4rem;
      border: .04rem solid;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      margin-bottom: .5rem;
    }

    .one {
      border-color: seagreen;
    }

    .two {
      border-color: skyblue;
    }

    .three {
      border-color: thistle;
    }

    .four {
      border-color: tomato;
    }

    .five {
      border-color: rosybrown;
    }

    .video-box>p {
      font-size: .2rem;
      color: sienna;
      margin-bottom: .2rem;
    }

    video {
      width: 2.8rem;
      height: 1.8rem;
      border-radius: .1rem;
      object-fit: cover;
    }
  </style>

</head>

<body>
  <h2 class="title">h5视频进入视口自动播放Demo</h2>
  <section class="video-box one">
    <p>模块一</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
  <section class="video-box two">
    <p>模块二</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
  <section class="video-box three">
    <p>模块三</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
  <section class="video-box four">
    <p>模块四</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
  <section class="video-box five">
    <p>模块五</p>
    <video
      controls
      loop="loop"
      x5-playsinline
      playsinline="true"
      x-webkit-airplay="allow"
      webkit-playsinline="true"
      x5-video-player-type="h5"
      x5-video-player-fullscreen="true"
      poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
      src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
    ></video>
  </section>
</body>

<script>
  function arrayLikeToArray(arrayLike) {
    return Array.from(arrayLike);
  }
  function commonIntersection(elements, cb) {
    const elementsObserve = new IntersectionObserver(entries => {
      entries.forEach(item => {
        if (item.isIntersecting) {
          cb(item);
        }
      })
    }, {
      threshold: [1],
    });
    if (Array.isArray(elements)) {
      elements.forEach(item => {
        elementsObserve.observe(item);
      })
    } else {
      elementsObserve.observe(elements);
    }
  }

  function setVideoAutoPlay() {
    // 视频出现在屏幕中间自动播放
    const sectionList = arrayLikeToArray(document.querySelectorAll('.video-box'));
    commonIntersection(sectionList, item => {
      const video = item.target.getElementsByTagName('video')[0];
      onlyOneVideoCanPlay(video);
    });
  }

  const videoList = arrayLikeToArray(document.querySelectorAll('video'));
  function onlyOneVideoCanPlay(video) {
    videoList.forEach(item => {
      if (item === video) {
        item.play();
      } else {
        item.pause();
      }
    })
  }

  setVideoAutoPlay();
</script>

</html> 

3. 参考链接