借助 Document Picture-in-Picture API,你可以打开一个始终可在顶部填充任意 HTML 内容的窗口。它扩展了现有的 <video>
的 Picture-in-Picture API,该 API 仅允许将 HTML <video>
元素放入画中画窗口中。
Document Picture-in-Picture API 中的“画中画”窗口类似于通过 window.open()
打开的空白同源窗口,但存在一些差异:
- “画中画”窗口悬浮在其他窗口之上。
- 画中画窗口的存在时间绝不会超过打开的窗口。
- 无法浏览“画中画”窗口。
- 网站无法设置画中画窗口位置。
当前状态
步骤 | 状态 |
---|---|
1. 创建铺垫消息 | 完成 |
2. 创建规范的初始草稿 | 进行中 |
3. 收集反馈并不断改进设计 | 进行中 |
4. 源试用 | 完成 |
5. 启动 | 完成(桌面设备) |
用例
自定义视频播放器
网站可以使用现有的 <video>
Picture-in-Picture API 来提供画中画视频体验,但此 API 非常有限。现有的画中画窗口只接受很少的输入,而且设置其样式的能力也有限。借助画中画功能的完整文档,网站可以提供自定义控件和输入(例如字幕、播放列表、时间滑块、顶和踩视频),以提升用户的画中画视频体验。
视频会议
用户在视频会议期间出于各种原因离开浏览器标签页(例如,在通话时显示其他标签页或处理多项任务)但仍然希望看到通话的情况,因此它是画中画的主要用例。同样,目前,视频会议网站可通过适用于 <video>
的 Picture-in-Picture API 提供的体验,在风格和输入方面都非常有限。借助画中画功能的完整文档,该网站可以轻松将多个视频流合并到一个画中画窗口中,而无需依赖画布技巧并提供自定义控件(例如发送消息、将其他用户静音或举手)。
效率
研究表明,用户需要通过更多方式在网络上保持工作效率。画中画功能可让 Web 应用灵活地完成更多任务。无论是文本编辑、记事、任务列表、即时通讯和聊天,还是设计和开发工具,Web 应用现在都能随时访问其内容。
接口
属性
documentPictureInPicture.window
- 返回当前的画中画窗口(如果有)。否则,返回
null
。
方法
documentPictureInPicture.requestWindow(options)
返回一个在打开画中画窗口时解析的 promise。 如果不使用用户手势进行调用,promise 会拒绝。
options
字典包含以下可选成员:width
- 设置画中画窗口的初始宽度。
height
- 设置画中画窗口的初始高度。
disallowReturnToOpener
- 如果值为 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,
});
});
将样式表复制到画中画窗口
如需复制源窗口中的所有 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
显示模式编写特定的 CSS 规则,这些规则仅在 Web 应用(的一部分)在画中画模式下显示时应用。
@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 Picture-in-Picture API VideoJS 播放器演示。请务必查看源代码。
番茄酱
Tomodoro 是一款番茄 Web 应用,也在 Document Picture-in-Picture API 的使用中加以利用(请参阅 GitHub 拉取请求)。
反馈
如果您有建议和问题,请在 GitHub 上提交问题。
实用链接
- 公开解说
- WICG 规范
- Chromium 跟踪错误
- ChromeStatus.com 条目
- Blink 组件:
Blink>Media>PictureInPicture
- 代码审核
- 有实验意向
- 发货意向
致谢
主打图片:Jakob Owens。