浏览器中的画中画(Picture-in-Picture)模式及其 API

简介:

想边刷微博边追剧?想边聊微信边看球赛?从浏览器支持扩展功能开始,人们就一直有这样的需求:

Chrome看视频,有没有可以弹出视频窗口的插件?

各个国产壳子浏览器也争相内置“视频弹窗播放”的功能,比如当年的火狐中国版

2700061cbfe5f04bc6812c6d370d967c92eaeadc

这么多年过去了,正统浏览器们终于要内置这个功能了,而且还有配套的 API 给开发者使用。

WICG,全名为 Web Incubator Community Group(Web 孵化器社区小组),主要成员为 Chrome 的工程师们,他们从一年前开始标准化这个能让视频弹窗播放的特性,起名为 Picture-in-Picture(画中画),缩写为 PiP。

Chrome 从今年年初开始着手实现,到本文撰稿时,已经实现的差不多了。这篇文章将主要讲两方面:Chrome 当前实现的 PiP 交互是怎么样的,以及有哪些 API 让我们开发者使用。

Chrome 中的 PiP 交互

Chrome 官方提供了一个 demo 页面 Picture-in-Picture Sample,我用这个 demo 录了一个简单的演示视频:


从视频中可以看到,Chrome 在原生的 video 控件中提供了开启画中画模式的菜单项,一旦开启画中画模式,原本页面中内联(inline)展现的那个 video 还占据着原来的位置,各种控件也都在,但最重要的视频画面已经被无缝的转移到了新的弹出窗口中了,只剩下一张 poster 图片和一层灰色的遮罩。

我们把弹出的那个视频窗口叫做 PiP 窗口,PiP 窗口只有视频画面,没有标题栏和地址栏(chrome-less),这一点和用 window.open()打开的弹窗不一样。同时,PiP 窗口也没有完整的视频控件,只有开始/暂停/关闭按钮三个,时间、进度条、音量这些控件都没有。PiP 窗口还支持拖拽改变大小(不能大于一个象限),以及支持拖拽到任意位置(录屏里没有体现,因为在本文撰写时还不支持)。还有最重要的一点是,PiP 窗口在所有窗口中是置顶的(always on top),正如我在录屏里演示的,当焦点切换到 Safari 后,Chrome 中打开的 PiP 窗口仍然是在最上层的。

另外一个没有在录屏里体现出来的交互是,“同一时刻只能有一个 PiP 窗口”。同一个页面里如果为多个视频开启画中画模式,前面开启的那个会自动退出画中画模式,同一个浏览器窗口下的多个 Tab 里的视频亦如此,同一个浏览器打开的多个浏览器窗口亦如此(在本文撰稿时,Chrome 还没有实现最后一种情况,即多个浏览器窗口可以同时开启多个 PiP 窗口,已确认是 bug 不是 feature)。

PiP 相关 API

1. video 元素新增的方法 requestPictureInPicture()

请求让该 video 元素进入画中画模式,返回一个 promise,如果没有异常,这个 promise 包的值会是一个 PictureInPictureWindow对象,这个对象就代表弹出的那个 PiP 窗口,后面会单独讲它的 API。


async function openPiP(video) {
  try {
    const pipWindow = await video.requestPictureInPicture() // 进入画中画模式
    ...
  } catch (e) {
    console.error(e) // 处理异常
  }
})

哪些情况下进入画中画模式会失败?一共有 5 种情况:

  1. 操作系统不支持、或者用户通过浏览器选项禁用了此功能,此时 document.pictureInPictureEnabled 属性会返回 false
  2. 视频文件错误、或者没有视频流只有音频流
  3. 此次请求不是由用户操作触发的,比如用户没有点击任何按钮,页面自动执行该方法,会被当做恶意行为拦截掉
  4. 当前页面通过 feature-policy 禁用了画中画特性,此时 document.pictureInPictureEnabled属性也会返回 false
  5. 当前 video 元素通过 disablePictureInPicture 属性(HTML 属性和 DOM 属性均可)禁用了画中画特性

2. video 元素新增的属性 disablePictureInPicture

通过该属性可以禁用 video 元素的画中画特性,右键菜单中的“画中画”选项会被禁用。

通过 HTML 属性:

<video src="..." disablePictureInPicture>

通过 DOM 属性:

video.disablePictureInPicture = true

3. video 元素新增的事件 enterpictureinpicture 和 leavepictureinpicture


video.addEventListener('enterpictureinpicture', function(pipWindow) {
  // 进入了画中画模式,可以拿到 pipWindow 对象
})

video.addEventListener('leavepictureinpicture', function() {
  // 退出了画中画模式
})

4. document 上新增的方法 exitPictureInPicture()

因为一个页面只能打开一个 PiP 窗口,所以让 video 元素退出画中画模式的方法不在 video 元素自己身上,而在 document 上。

这个方法也返回一个 promise,不过 promise 包的值是个 undefined

5. document 上新增的属性 pictureInPictureElement和 pictureInPictureEnabled

类似于document.pointerLockElement和 document.fullscreenElement, document.pictureInPictureElement 会返回当前页面中处于画中画模式的 video 元素,如果没有的话,返回 null

document.pictureInPictureEnabled上面已经提到过了,在当前页面不支持或被禁用画中画模式的情况下会返回 false,否则返回 true

这两个属性都是只读的。

6. PictureInPictureWindow对象的 API

requestPictureInPicture()方法的返回值和 enterpictureinpicture事件的回调参数中可以拿到 pipWindow 对象,该对象有两个属性 widthheight,还支持一个resize事件,在用户改变 PiP 窗口大小时会触发。


async function openPiP(video) {
  const pipWindow = await video.requestPictureInPicture()
  console.log(pipWindow.width, pipWindow.height) // 打印了默认的窗口大小

  pipWindow.addEventListener('resize', function() {
    console.log(pipWindow.width, pipWindow.height) // 用户改变 PiP 窗口大小时触发
  })
}

注意这里的 widthheight是只读的,你不能通过给他们赋值来改变窗口大小。

杂项

1. Safari 中已有的画中画 API

Safari 在两年前就支持了画中画模式,还有一套带前缀的配套 API,叫 Presentation Mode。好消息是这个新规范也有 Safari 的人参与了制定,事实上目前为止四大浏览器厂商只剩 Edge 还没表示支持。

2. 画中画这个名字的由来

“画中画”这东西是十几年前电视机上的一个功能,我自己去年看到这个规范的时候也觉的它这个名字比较误导人,现在规范 issue 里也有个人在问,希望你没把它理解成是嵌套的 <img>

3. Chrome 哪个版本支持

现在的 Chrome Canary(69)还没有默认打开这个特性,需要你手动开启

chrome://flags/#enable-experimental-web-platform-features

chrome://flags/#disable-background-video-track

chrome://flags/#enable-picture-in-picture

这三个开关,不用 Canary 的同学老实等两个月。

4. 可不可能弹出 video 以外的元素

规范制定者表示未来有可能


原文发布时间为:2018年06月21日
原文作者: 掘金

本文来源: 掘金 如需转载请联系原作者

相关文章
|
3月前
|
存储 前端开发 开发者
|
3月前
|
Web App开发 iOS开发 MacOS
如何在浏览器中启用夜间模式?
【10月更文挑战第10天】
|
3月前
|
缓存 监控 API
探索微服务架构中的API网关模式
【10月更文挑战第5天】随着微服务架构的兴起,企业纷纷采用这一模式构建复杂应用。在这种架构下,应用被拆分成若干小型、独立的服务,每个服务围绕特定业务功能构建并通过HTTP协议协作。随着服务数量增加,统一管理这些服务间的交互变得至关重要。API网关作为微服务架构的关键组件,承担起路由请求、聚合数据、处理认证与授权等功能。本文通过一个在线零售平台的具体案例,探讨API网关的优势及其实现细节,展示其在简化客户端集成、提升安全性和性能方面的关键作用。
85 2
|
3月前
|
存储 缓存 监控
探索微服务架构中的API网关模式
【10月更文挑战第1天】探索微服务架构中的API网关模式
101 2
|
5月前
|
JavaScript 前端开发 API
[译] 用 Vue 3 Composition API 实现 React Context/Provider 模式
[译] 用 Vue 3 Composition API 实现 React Context/Provider 模式
|
2月前
|
缓存 负载均衡 JavaScript
探索微服务架构下的API网关模式
【10月更文挑战第37天】在微服务架构的海洋中,API网关犹如一座灯塔,指引着服务的航向。它不仅是客户端请求的集散地,更是后端微服务的守门人。本文将深入探讨API网关的设计哲学、核心功能以及它在微服务生态中扮演的角色,同时通过实际代码示例,揭示如何实现一个高效、可靠的API网关。
|
2月前
|
缓存 监控 API
探索微服务架构中的API网关模式
随着微服务架构的兴起,API网关成为管理和服务间交互的关键组件。本文通过在线零售公司的案例,探讨了API网关在路由管理、认证授权、限流缓存、日志监控和协议转换等方面的优势,并详细介绍了使用Kong实现API网关的具体步骤。
54 3
|
2月前
|
存储 缓存 监控
探索微服务架构中的API网关模式
探索微服务架构中的API网关模式
57 2
|
2月前
|
Web App开发 API Windows
取接口访问者信息[IP,浏览器,操作系统]免费API接口教程
此API用于获取访问者的IP地址、浏览器和操作系统信息,支持70多种浏览器和操作系统。通过POST或GET请求至`https://cn.apihz.cn/api/ip/getapi.php`,需提供用户ID和KEY。返回结果包括状态码、消息、IP、浏览器和操作系统信息。示例:{&quot;code&quot;:200,&quot;ip&quot;:&quot;175.154.88.178&quot;,&quot;browser&quot;:&quot;Chrome&quot;,&quot;os&quot;:&quot;Windows 10&quot;}。详情见官网文档。
|
4月前
|
存储 API 数据库
如何使用 ef core 的 code first(fluent api)模式实现自定义类型转换器?
本文介绍了如何在 EF Core 的 Code First 模式下使用自定义类型转换器实现 JsonDocument 和 DateTime 类型到 SQLite 数据库的正确映射。通过自定义 ValueConverter,实现了数据类型的转换,并展示了完整的项目结构和代码实现,包括实体类定义、DbContext 配置、Repositories 仓储模式及数据库应用迁移(Migrations)操作。
81 6
如何使用 ef core 的 code first(fluent api)模式实现自定义类型转换器?