为视频增加画中画功能 -- Picture-in-Picture API详解

1,138 阅读3分钟

photo-1530251985675-fa6a8ceb0d63.jpg


画中画(Picture-in-Picture,以下简称PiP)想必混迹各大视频网站的朋友应该很熟悉,它是一种视频播放技术,可以将网页上的视频内容从主窗口中分离出来并在一个浮动窗口中播放。这可以让用户在其它页面或者应用程序观看主要视频或者其他内容的同时,保持对辅助视频内容的关注。比如你在开视频会议时能通过小视频窗口看番摸鱼。比如:

image.png

咳咳咳...千万不要像我一样双线程学习哈🤫。

PiP API是在18年左右在部分主流浏览器支持,MDN对其的描述是这样的:

Picture-in-Picture API允许网站总是在其他窗口之上创建一个浮动的视频,以便用户在其他内容站点或者设备上的应用程序交互时可以继续播放媒体。

目前主要还是在PC端的浏览器兼容性会好一点:

image.png

移动端H5的话,通常浏览器会自带一个网页视频助手,包含全屏播放、小屏播放和视频下载等等功能...至少我的华为自带的浏览器是有的,遥遥领先!😎

PiP API主要内容包括:一个接口、八个扩展和一个伪类。接下来我们将一一学习它们。

温馨提示:根据PiP API规范的说明,通常支持PiP API的操作系统目前只能有一个画中画窗口,当已经有画中画窗口时,再请求进入画中画模式的话会挤掉原来的视频,也有可能会拒绝请求。这些将来可能会由实现方式和平台决定。

PictureInPictureWindow接口

PictureInPictureWindow接口就表示进入画中画模式时的那个浮动视频窗口(以下简称小窗口),用于获取小窗口的尺寸和监听尺寸变化。

image.png

属性/事件

属性/事件简介
width只读属性,用于获取小窗口的宽度。
height只读属性,用于获取小窗口的高度。
onresize监听小窗口的尺寸变化事件。

HTMLVideoElement.requestPictureInPicture()方法

可以通过HTMLVideoElement.requestPictureInPicture()异步方法会发出异步请求将视频以画中画模式显示。同时解析成功时其.then(pictureInPictureWindow)方法中的参数就是一个PictureInPictureWindow实例对象。

注意:该方法必须由用户交互激活之后才会正常工作。

document.exitPictureInPicture()方法

通常小窗口默认会有一个“返回到标签页”的按钮来从画中画面试恢复到传统模式中,这个功能也可以通过document.exitPictureInPicture()异步方法来实现。该方法会反转HTMLVideoElement.requestPictureInPicture()方法的结果,通常用于在传统和画中画模式之间切换,以及在恢复到传统模式时通过其.catch()方法处理发生的异常错误等等。

🌰:

<video src="xxx" controls></video>
<button>进入画中画模式</button>

<script>
  const myVideo = document.querySelector('video');
  const pipBtn = document.querySelector('button');

  pipBtn.addEventListener('click', () => {
    if (document.pictureInPictureElement) {
      // 如果已经在PIP模式下,则退出PIP
      document.exitPictureInPicture()
    } else {
      // 启用PIP
      myVideo.requestPictureInPicture().then(pipw => {
        pipw.addEventListener('resize', e => {
          console.log(`${e.target.width}, ${e.target.height}`)
        })
      })
    }
  });
</script>

其它接口上的扩展

PiP API在HTMLVideoElementDocumentShadowRoot这三个接口上进行了扩展,包括:

属性/事件扩展接口简介
disablePictureInPictureHTMLVideoElement用于指定当前<video>元素是(true)否(false)禁用画中画功能。
pictureInPictureEnabledDocument只读属性,指示当前文档画中画功能是否可用。
pictureInPictureElementDocumentShadowRoot只读属性,用于获取文档或Shadow Tree中处于画中画模式的那个元素。
enterpictureinpictureHTMLVideoElement事件,在进入画中画模式时触发。
leavepictureinpictureHTMLVideoElement事件,在退出画中画模式时触发。
resizeHTMLVideoElement事件,在小窗口尺寸变化时触发。跟上面PictureInPictureWindow接口的onresize事件其实是一样的。

注意:最后三个事件不可取消也不会冒泡。

🌰:

<my-video></my-video>

<script>
  class MyVideo extends HTMLElement {
   connectedCallback() {
      const root = this.attachShadow({mode: 'open'});

      const video = document.createElement("video");
      video.src = 'xxx'
      video.width = '500'

      console.log(video.disablePictureInPicture)
      console.log(document.pictureInPictureEnabled)

      video.addEventListener('enterpictureinpicture', () => {
        console.log('已进入画中画模式')
      })
      video.addEventListener('leavepictureinpicture', () => {
        console.log('已退出画中画模式')
      })

      const button = document.createElement("button");
      button.innerText = '进入/退出画中画模式'
      button.addEventListener('click', () => {
        video.requestPictureInPicture().then(pipw => {
          pipw.addEventListener('resize', e => {
            console.log(root.pictureInPictureElement)
            console.log(`${e.target.width}, ${e.target.height}`)
          })
        })
      })
        
      root.appendChild(video)
      root.appendChild(button)
    }
  }
    
  // 注册元素
  customElements.define('my-video', MyVideo)
</script>

:picture-in-picture伪类

:picture-in-picture伪类匹配进入画中画模式后留在原地的那个元素。通常用于在画中画和传统模式之间来回切换时自动调整内容的大小、样式或布局。

image.png

注意:如果是配合Web Component使用需要在内部使用才有效。

参考资料