一探究竟bilibili自动进入画中画视频小窗继续播放

背景

b站相信大家都不陌生,那你知道如何实现b站PC端播放时,滚动条下拉直至隐藏视频播放主窗口后,自动进入小窗播放是如何实现的吗?下文将以此为目标,探索其自动进入小窗视频播放窗口的实现。

视频画中画API

画中画 APIPicture-in-Picture API)可以使你的网站内的video标签播放的视频可以暂时脱离浏览器窗口,并悬浮在你的电脑窗口应用上,让你可以切换到其他网站或者隐藏浏览器时依然可以继续观看视频。

下面是一个简单使用画中画API的小case

html 复制代码
<body>
  <div>
    <h2>画中画</h2>
    <video src="./e0ab60c2d692246aff03211b457506f1.mp4" controls></video>
    <div><button onclick="togglePictureInPicture('')">切换画中画</button></div>
  </div>
  <script>
    // 切换画中画方法
    function togglePictureInPicture() {
      const video = document.querySelector('video');
      console.log('togglePictureInPicture');
      if (document.pictureInPictureElement) {
        document.exitPictureInPicture();
      } else {
        if (document.pictureInPictureEnabled) {
          video.requestPictureInPicture();
        }
      }
    }
  </script>
</body>

注意 :调用video.requestPictureInPicture()方法必须在监听用户手势操作的回调方法中执行才会生效。不能在用户不知情的情况下调用,如视频窗口在页面上不可见时自动开启小窗模式,这个API无法实现 的!如果这样做你将得到以下错误:

交叉观察器API

IntersectionObserver交叉观察器 接口提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根(root)。

下面是一个简单使用交叉观察器API的小case

js 复制代码
const video = document.getElementById('video');
  const intersectionObserver = new IntersectionObserver((entries) => {
    // 如果intersectionRatio属性小于或等于0,则目标在视野外
    if (entries[0].intersectionRatio <= 0) {
      console.log('video在视野之外')
    } else {
      console.log("video在视野之内");
    }
  });
  // 开始监听
  intersectionObserver.observe(video);

不管是浏览器滚动条 或是页面元素滚动条 滚动到所观察的元素不可见 的位置,简单地说就说元素从窗口消失了 都可以使用该API观察到。

浏览器滚动条

页面元素滚动条

学会交叉观察器 API的用法后,后面实现模拟 自动开启画中画小窗播放视频那也就信手拈来啦。

模拟b站自动小窗视频播放实战

由于前面的画中画 API只能在监听用户的手势操作回调函数当中触发,无法实现在交叉观察器的回调函数 当中触发,所以为了实现更灵活的目的,可以通过定位功能,模拟一个画中画窗口,但灵活的代价是只能在页面当中进行观看,模拟窗口无法像原生画中画API那样脱离浏览器窗口依然还能继续观看视频。

实现代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>画中画小案例</title>

  <style>
    :root {
      --p-in-p-width: 1000px;
      --p-in-p-height: 500px;
    }

    .video-container {
      width: var(--p-in-p-width);
      height: var(--p-in-p-height);
      ;
    }

    .video,
    .mini-video {
      width: 100%;
      height: 100%;
    }

    .mini-container {
      width: calc(var(--p-in-p-width) / 3);
      height: calc(var(--p-in-p-height) / 3);
    }

    .p-in-p {
      display: none;
      position: fixed;
      bottom: 50px;
      right: 50px;
      background-color: rgba(0, 0, 0, 0.5);
      z-index: 9999;
    }
  </style>
</head>

<body style="height: 2000px;">

  <div class="video-container">
    <video class="video" src="./e0ab60c2d692246aff03211b457506f1.mp4" controls></video>
  </div>

  <div class="p-in-p">
    <div>画中画小窗</div>
    <div class="mini-container">

    </div>
    <div><button class="closeBtn">关闭画中画</button></div>
  </div>

  <script>
    window.onload = function () {
      const videoContainer = document.querySelector('.video-container');
      const video = document.querySelector('.video');
      const miniVideoContainer = document.querySelector('.mini-container');
      const PInPWindow = document.querySelector('.p-in-p');

      const intersectionObserver = new IntersectionObserver((entries) => {
      // 如果intersectionRatio属性小于或等于0,则目标在视野外
      if (entries[0].intersectionRatio <= 0) {
        console.log('video在视野之外')
        miniVideoContainer.appendChild(video);
        PInPWindow.style.display = 'block';
      } else {
        console.log("video在视野之内");
        videoContainer.appendChild(video);
        PInPWindow.style.display = 'none';
      }
    });

      // 开始监听
      intersectionObserver.observe(videoContainer);

      // 手动关闭画中画
      const closeBtn = document.querySelector('.closeBtn');
      closeBtn.addEventListener('click', () => {
        console.log('togglePictureInPicture');
        PInPWindow.style.display = 'none';
      });

      // 页面卸载监听
      window.onunload = function () {
        // 终止所有监听
        intersectionObserver.disconnect();
      }
    }
  </script>
</body>

</html>

实现原理

当视频播放的主窗口隐藏后,把主窗口的video标签移动到小窗口上继续播放,当主播放窗口出现时,把video标签放回原来位置,再隐藏小窗。

实现效果动图

总结

首先了解了浏览器原生视频画中画 API的用法,发现无法满足在隐式状态下自动进入视频画中画小窗播放需求。

其次学了交叉观察器 的用法,为后续手动模拟实现一个类似b站主窗口播放 隐藏自动进入小窗播放的效果。

最后通过一个简单实战案例 ,成功模拟了b站的主窗口播放隐藏自动进入小窗播放的效果。

相关推荐
Hy行者勇哥4 小时前
现代软件系统架构:前端、后端、数据库、部署、算法与AI学习的结构与交互分析
前端·数据库·学习
前端开发爱好者5 小时前
90% 前端都不知道的 20 个「零依赖」浏览器原生能力!
前端·javascript·vue.js
讨厌吃蛋黄酥5 小时前
React语法全景指南:面试官问我用了哪些语法时,我这样回答拿到了offer
前端·react.js·面试
Bling_Bling_15 小时前
面试常考css:三列布局实现方式
前端·html·css3
讨厌吃蛋黄酥6 小时前
Promise的底层揭秘:微任务与观察者模式的完美共舞
前端·javascript·面试
咔咔一顿操作6 小时前
第五章 vue3 + Three.js 实现高级镜面反射效果案例解析
前端·javascript·vue.js·人工智能·信息可视化·threejs
码上心间7 小时前
树形结构后端构建
java·前端·javascript·vue.js
挖稀泥的工人7 小时前
如何在Eletron中打开window的powershell
前端·electron·shell