原生画中画API:让视频飞出浏览器窗口
视频明明在网页里放着,用户一切到别的标签页,内容就“失踪”了。这种体验我一直不太接受。尤其是后台盯监控、边查文档边看直播、边记笔记边听课程这类场景,明明浏览器已经有现成能力,很多前端页面还在自己搓悬浮窗,越做越重。
其实画中画这事,前端真没多复杂。原生API就够用,几行JS,视频就能从网页里“飞”出去。
最小可用版本
const video = document.querySelector('#player');
const pipBtn = document.querySelector('#pipBtn');
pipBtn.addEventListener('click', async () => {
try {
if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
return;
}
if (document.pictureInPictureEnabled && video.readyState > 0) {
await video.requestPictureInPicture();
}
} catch (err) {
console.error('进入画中画失败:', err.message);
}
});核心就两个方法:
video.requestPictureInPicture()
document.exitPictureInPicture()
就这两个,别一上来整一堆状态机。很多页面把简单功能写复杂,问题不在API,在自己先把自己吓住了。
几个容易踩的坑
1. 必须是video元素
你拿一个普通div或者canvas直接调,肯定不行。这个能力本质上还是围着媒体元素转的。很多人页面上看着像播放器,实际外面套了三层壳,真要进画中画,还是得拿到里面那个<video>。
HTML大概像这样:
<video
id="player"
src="/assets/demo.mp4"
controls
playsinline
preload="metadata"
style="width: 640px"
></video>
<button id="pipBtn">打开小窗</button>2. 用户手势触发
这点很关键。你在接口回调里、定时器里、自动播放逻辑里直接调用,大概率会被浏览器拦下来。这个能力通常要求由用户主动点击触发。所以我一般不在自动逻辑里搞,老老实实给个按钮,最省事。
3. 状态要跟着事件走
video.addEventListener('enterpictureinpicture', () => {
console.log('已经进入画中画');
pipBtn.textContent = '关闭小窗';
});
video.addEventListener('leavepictureinpicture', () => {
console.log('已经退出画中画');
pipBtn.textContent = '打开小窗';
});这里我一般会直接监听事件,不太喜欢自己维护一份“是否画中画”的前端状态。浏览器都告诉你进没进了,还自己存个布尔值,后面切标签、切视频源、用户手动关窗口,状态一漂就开始对不上。
多视频场景的处理
真实业务里经常不是单视频页面,而是列表页。比如课程列表、会议回放、监控大盘,同时能看到多个视频卡片。这个时候你不能让每个按钮都各管各的,不然一会儿这个进,一会儿那个抢,页面就乱了。
我一般会这么处理:
async function togglePip(videoEl) {
try {
const current = document.pictureInPictureElement;
if (current && current !== videoEl) {
await document.exitPictureInPicture();
}
if (document.pictureInPictureElement === videoEl) {
await document.exitPictureInPicture();
} else {
await videoEl.requestPictureInPicture();
}
} catch (e) {
console.error('切换画中画失败:', e);
}
}这个处理不花哨,但够稳。先把现场清干净,再让当前视频进去。很多bug不是API本身有坑,是页面上同时存在多个播放器,开发时默认“用户只会点一个”,上线后用户偏不。
切换视频源时的处理
还有一个点经常被忽略:切源时要退画中画。比如用户正在小窗播放A视频,这时候列表里点了B视频,你如果直接改src,不同浏览器表现可能不一样,轻则黑屏,重则按钮状态错乱。保险写法是先退,再切,再按需重进。
async function switchVideo(video, nextSrc) {
if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
}
video.src = nextSrc;
await video.play();
}适合画中画的场景
画中画这个能力很适合几类页面:
在线教育
直播回放
视频客服
监控预览
边看边操作的后台系统
它不是炫技功能,是能改善停留时长和操作连续性的。用户不用来回切标签,视频内容也不会因为页面失焦直接废掉。
最后补一句,不要自己上来就做draggable悬浮播放器。能用浏览器原生能力解决的事,先别给自己加维护成本。原生画中画已经帮你处理了窗口层级、拖动、置顶这些问题,前端只需要把入口和状态处理好。
这功能真不算大活。一个按钮,一段事件绑定,网页里的视频就能飞出浏览器。写完你再回头看,会发现之前很多播放器需求,其实压根没必要做那么重。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!