使用文档画中画 API

实验性: 这是一个 实验性技术
在生产环境中使用此功能之前,请仔细查看 浏览器兼容性表

安全上下文: 此功能仅在 安全上下文(HTTPS)中可用,在某些或所有 支持的浏览器中可用。

本指南提供了 文档画中画 API 的典型用法的演练。

注意: 您可以在 文档画中画 API 示例 中看到功能演示(也可以查看完整的 源代码)。

示例 HTML

以下 HTML 设置了一个基本的视频播放器。

html
<div id="container">
  <p class="in-pip-message">
    Video player is currently in the separate Picture-in-Picture window.
  </p>
  <div id="player">
    <video
      src="assets/bigbuckbunny.mp4"
      id="video"
      controls
      width="320"></video>

    <div id="credits">
      <a href="https://peach.blender.org/download/" target="_blank">
        Video by Blender </a
      >;
      <a href="https://peach.blender.org/about/" target="_blank">
        licensed CC-BY 3.0
      </a>
    </div>

    <div id="controlbar">
      <p class="no-picture-in-picture">
        Document Picture-in-Picture API not available
      </p>

      <p></p>
    </div>
  </div>
</div>

特性检测

要检查是否支持文档画中画 API,您可以测试 window 上是否可以使用 documentPictureInPicture

js
if ("documentPictureInPicture" in window) {
  document.querySelector(".no-picture-in-picture").remove();

  const togglePipButton = document.createElement("button");
  togglePipButton.textContent = "Toggle Picture-in-Picture";
  togglePipButton.addEventListener("click", togglePictureInPicture, false);

  document.getElementById("controlbar").appendChild(togglePipButton);
}

如果可用,我们将删除“文档画中画 API 不可用”消息,而是添加一个 <button> 元素以在文档画中画窗口中打开视频播放器。

打开画中画窗口

以下 JavaScript 调用 window.documentPictureInPicture.requestWindow() 以打开一个空白的画中画窗口。返回的 Promise 使用画中画 Window 对象完成。视频播放器使用 Element.append() 移动到该窗口,我们显示消息通知用户它已移动。

requestWindow()widthheight 选项将画中画窗口设置为所需大小。如果浏览器选项值过大或过小以至于无法适应用户友好的窗口大小,则浏览器可能会限制选项值。

js
async function togglePictureInPicture() {
  // Early return if there's already a Picture-in-Picture window open
  if (window.documentPictureInPicture.window) {
    return;
  }

  // Open a Picture-in-Picture window.
  const pipWindow = await window.documentPictureInPicture.requestWindow({
    width: videoPlayer.clientWidth,
    height: videoPlayer.clientHeight,
  });

  // ...

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(videoPlayer);

  // Display a message to say it has been moved
  inPipMessage.style.display = "block";
}

将样式表复制到画中画窗口

要复制来自原始窗口的所有 CSS 样式表,请循环遍历显式链接到或嵌入到文档中的所有样式表(通过 Document.styleSheets)并将它们附加到画中画窗口。请注意,这是一次性复制。

js
// ...

// Copy style sheets over from the initial document
// so that the player looks the same.
[...document.styleSheets].forEach((styleSheet) => {
  try {
    const cssRules = [...styleSheet.cssRules]
      .map((rule) => rule.cssText)
      .join("");
    const style = document.createElement("style");

    style.textContent = cssRules;
    pipWindow.document.head.appendChild(style);
  } catch (e) {
    const link = document.createElement("link");

    link.rel = "stylesheet";
    link.type = styleSheet.type;
    link.media = styleSheet.media;
    link.href = styleSheet.href;
    pipWindow.document.head.appendChild(link);
  }
});

// ...

在画中画模式下定位样式

display-mode 媒体特征picture-in-picture 值允许开发人员根据文档是否以画中画模式显示来应用 CSS。基本用法如下所示

css
@media (display-mode: picture-in-picture) {
  body {
    background: red;
  }
}

此代码段将仅在文档以画中画模式显示时,将文档 <body> 的背景颜色更改为红色。

我们的演示 中,我们将 display-mode: picture-in-picture 值与 prefers-color-scheme 媒体特征结合使用,以创建亮色和暗色配色方案,这些方案根据用户的配色方案偏好应用,仅当应用程序在画中画模式下显示时才应用。

css
@media (display-mode: picture-in-picture) and (prefers-color-scheme: light) {
  body {
    background: antiquewhite;
  }
}

@media (display-mode: picture-in-picture) and (prefers-color-scheme: dark) {
  body {
    background: #333;
  }

  a {
    color: antiquewhite;
  }
}

处理画中画窗口关闭时的操作

再次按下按钮时,用于切换画中画窗口关闭的代码如下所示

js
inPipMessage.style.display = "none";
playerContainer.append(videoPlayer);
window.documentPictureInPicture.window.close();

在这里,我们反转了 DOM 更改——隐藏消息并将视频播放器放回主应用程序窗口的播放器容器中。我们还使用 Window.close() 方法以编程方式关闭画中画窗口。

但是,您还需要考虑用户通过按下窗口本身的浏览器提供的关闭(X)按钮关闭画中画窗口的情况。您可以通过使用 pagehide 事件检测窗口关闭来处理这种情况

js
pipWindow.addEventListener("pagehide", (event) => {
  inPipMessage.style.display = "none";
  playerContainer.append(videoPlayer);
});

监听网站进入画中画时的操作

侦听 DocumentPictureInPicture 实例上的 enter 事件,以了解何时打开画中画窗口。

在我们的演示中,我们使用 enter 事件向画中画窗口添加静音切换按钮

js
documentPictureInPicture.addEventListener("enter", (event) => {
  const pipWindow = event.window;
  console.log("Video player has entered the pip window");

  const pipMuteButton = pipWindow.document.createElement("button");
  pipMuteButton.textContent = "Mute";
  pipMuteButton.addEventListener("click", () => {
    const pipVideo = pipWindow.document.querySelector("#video");
    if (!pipVideo.muted) {
      pipVideo.muted = true;
      pipMuteButton.textContent = "Unmute";
    } else {
      pipVideo.muted = false;
      pipMuteButton.textContent = "Mute";
    }
  });

  pipWindow.document.body.append(pipMuteButton);
});

注意: DocumentPictureInPictureEvent 事件对象包含一个 window 属性,用于访问画中画窗口。

访问元素和处理事件

您可以通过几种不同的方式访问画中画窗口中的元素

js
const pipWindow = window.documentPictureInPicture.window;
if (pipWindow) {
  // Mute video playing in the Picture-in-Picture window.
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
}

获得画中画 window 实例的引用后,您可以像在常规浏览器窗口上下文中一样操作 DOM(例如创建按钮)并响应用户输入事件(例如 click)。