借助文档画中画 API,您可以打开一个始终位于顶部的窗口,该窗口可以填充任意 HTML 内容。它扩展了现有的 Picture-in-Picture API(适用于 <video>),该 API 仅允许将 HTML <video> 元素放入画中画窗口中。
Document Picture-in-Picture API 中的画中画窗口类似于通过 window.open() 打开的空白同源窗口,但存在一些差异:
- 画中画窗口会浮动在其他窗口之上。
 - 画中画窗口的生命周期永远不会超过打开窗口的生命周期。
 - 无法在画中画窗口中导航。
 - 网站无法设置画中画窗口位置。
 
  当前状态
| 步骤 | 状态 | 
|---|---|
| 1. 创建说明 | 完成 | 
| 2. 创建规范的初始草稿 | 进行中 | 
| 3. 收集反馈并迭代设计 | 进行中 | 
| 4. 源试用 | 完成 | 
| 5. 启动 | 完成(桌面设备) | 
使用场景
自定义视频播放器
网站可以使用现有的 Picture-in-Picture API for <video> 提供画中画视频体验,但这种体验非常有限。现有的画中画窗口接受的输入较少,并且对这些输入进行样式设置的能力有限。借助画中画模式下的完整文档,网站可以提供自定义控件和输入内容(例如字幕、播放列表、时间滑块、视频点赞和不喜欢功能),从而改善用户的画中画视频体验。
视频会议
用户在视频会议期间出于各种原因(例如,向通话对象展示另一个标签页或执行多项任务)离开浏览器标签页是很常见的,但他们仍然希望看到通话内容,因此画中画功能非常适合这种使用情形。再次强调,视频会议网站目前通过 Picture-in-Picture API for <video> 提供的体验在样式和输入方面受到限制。借助完整的画中画文档,网站可以轻松地将多个视频流合并到单个画中画窗口中,而无需依赖 canvas hack,并提供自定义控件,例如发送消息、将其他用户静音或举手。
效率
研究表明,用户需要更多方式来提高网络工作效率。画中画文档功能让 Web 应用能够灵活地完成更多任务。无论是文本编辑、记笔记、任务清单、即时通讯和聊天,还是设计和开发工具,Web 应用现在都可以让用户随时访问其内容。
接口
属性
documentPictureInPicture.window- 返回当前的画中画窗口(如果有)。否则,返回 
null。 
方法
documentPictureInPicture.requestWindow(options)返回一个在画中画窗口打开时解析的 promise。如果未通过用户手势调用,则 Promise 会被拒绝。
options字典包含以下可选成员:width- 设置画中画窗口的初始宽度。
 height- 设置画中画窗口的初始高度。
 disallowReturnToOpener- 如果为 true,则隐藏画中画窗口中的“返回到标签页”按钮。默认值为 false。
 preferInitialWindowPlacement- 如果为 true,则以默认位置和大小打开画中画窗口。默认值为 false。
 
事件
documentPictureInPicture.onenter- 当画中画窗口打开时,在 
documentPictureInPicture上触发。 
示例
以下 HTML 代码用于设置自定义视频播放器和一个按钮元素,以在画中画窗口中打开视频播放器。
<div id="playerContainer">
  <div id="player">
    <video id="video"></video>
  </div>
</div>
<button id="pipButton">Open Picture-in-Picture window</button>
打开画中画窗口
以下 JavaScript 代码会在用户点击按钮时调用 documentPictureInPicture.requestWindow(),以打开一个空白的画中画窗口。返回的 promise 会解析为画中画窗口 JavaScript 对象。使用 append() 将视频播放器移至该窗口。
pipButton.addEventListener('click', async () => {
  const player = document.querySelector("#player");
  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();
  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});
设置画中画窗口的大小
如需设置画中画窗口的大小,请将 documentPictureInPicture.requestWindow() 的 width 和 height 选项设置为所需的画中画窗口大小。如果选项值过大或过小,无法适应用户友好的窗口大小,Chrome 可能会限制这些值。
pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");
  // Open a Picture-in-Picture window whose size is
  // the same as the player's.
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: player.clientWidth,
    height: player.clientHeight,
  });
  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});
隐藏画中画窗口的“返回标签页”按钮
如需隐藏画中画窗口中允许用户返回打开器标签页的按钮,请将 documentPictureInPicture.requestWindow() 的 disallowReturnToOpener 选项设置为 true。
pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window which hides the "back to tab" button.
  const pipWindow = await documentPictureInPicture.requestWindow({
    disallowReturnToOpener: true,
  });
});
以默认位置和大小打开画中画窗口
如需不重复使用上一个画中画窗口的位置或大小,请将 documentPictureInPicture.requestWindow() 的 preferInitialWindowPlacement 选项设置为 true。
pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window in its default position / size.
  const pipWindow = await documentPictureInPicture.requestWindow({
    preferInitialWindowPlacement: true,
  });
});
将样式表复制到画中画窗口
如需复制源窗口中的所有 CSS 样式表,请遍历显式链接到文档或嵌入到文档中的 styleSheets,然后将其附加到画中画窗口。请注意,这是一次性复制。
pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");
  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();
  // 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);
    }
  });
  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});
处理画中画窗口关闭时的情况
监听窗口的 "pagehide" 事件,以了解画中画窗口何时关闭(无论是因网站启动而关闭,还是因用户手动关闭而关闭)。事件处理程序是让元素重新退出画中画窗口的好地方,如下所示。
pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");
  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();
  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
  // Move the player back when the Picture-in-Picture window closes.
  pipWindow.addEventListener("pagehide", (event) => {
    const playerContainer = document.querySelector("#playerContainer");
    const pipPlayer = event.target.querySelector("#player");
    playerContainer.append(pipPlayer);
  });
});
使用 close() 方法以编程方式关闭画中画窗口。
// Close the Picture-in-Picture window programmatically. 
// The "pagehide" event will fire normally.
pipWindow.close();
在网站进入画中画模式时收听
监听 documentPictureInPicture 上的 "enter" 事件,以了解画中画窗口何时打开。该事件包含一个 window 对象,用于访问画中画窗口。
documentPictureInPicture.addEventListener("enter", (event) => {
  const pipWindow = event.window;
});
访问画中画窗口中的元素
通过 documentPictureInPicture.requestWindow() 返回的对象或使用 documentPictureInPicture.window 访问画中画窗口中的元素,如下所示。
const pipWindow = documentPictureInPicture.window;
if (pipWindow) {
  // Mute video playing in the Picture-in-Picture window.
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
}
处理画中画窗口中的事件
创建按钮和控件,并像在 JavaScript 中一样响应用户输入事件(例如 "click")。
// Add a "mute" button to the Picture-in-Picture window.
const pipMuteButton = pipWindow.document.createElement("button");
pipMuteButton.textContent = "Mute";
pipMuteButton.addEventListener("click", () => { 
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
});
pipWindow.document.body.append(pipMuteButton);
调整画中画窗口的大小
使用 resizeBy() 和 resizeTo() Window 方法调整画中画窗口的大小。这两种方法都需要用户手势。
const resizeButton = pipWindow.document.createElement('button');
resizeButton.textContent = 'Resize';
resizeButton.addEventListener('click', () => {
  // Expand the Picture-in-Picture window's width by 20px and height by 30px.
  pipWindow.resizeBy(20, 30);
});
pipWindow.document.body.append(resizeButton);
聚焦于打开器窗口
使用 focus() Window 方法从画中画窗口聚焦到打开程序窗口。此方法需要用户手势。
const returnToTabButton = pipWindow.document.createElement("button");
returnToTabButton.textContent = "Return to opener tab";
returnToTabButton.addEventListener("click", () => {
  window.focus();
});
pipWindow.document.body.append(returnToTabButton);
CSS 画中画显示模式
使用 CSS picture-in-picture 显示模式可编写仅在(部分)Web 应用以画中画模式显示时应用的特定 CSS 规则。
@media all and (display-mode: picture-in-picture) {
  body {
    margin: 0;
  }
  h1 {
    font-size: 0.8em;
  }
}
功能检测
如需检查是否支持 Document Picture-in-Picture API,请使用:
if ('documentPictureInPicture' in window) {
  // The Document Picture-in-Picture API is supported.
}
演示
VideoJS 播放器
您可以试用 Document 画中画 API VideoJS 播放器演示。
Pomodoro
Tomodoro 是一款番茄工作法 Web 应用,也会在可用时利用 Document Picture-in-Picture API。请参阅其 GitHub 拉取请求。
  分享您的反馈
在 GitHub 上提交问题,并附上建议和问题。