背景
b站
相信大家都不陌生,那你知道如何实现b站
在PC
端播放时,滚动条下拉直至隐藏视频播放主窗口后,自动进入小窗播放是如何实现的吗?下文将以此为目标,探索其自动进入小窗视频播放窗口的实现。
视频画中画API
画中画 API (Picture-in-Picture API)可以使你的网站内的video
标签播放的视频可以暂时脱离浏览器窗口,并悬浮在你的电脑窗口应用上,让你可以切换到其他网站或者隐藏浏览器时依然可以继续观看视频。
下面是一个简单使用画中画API
的小case
:
html
<body>
<div>
<h2>画中画</h2>
<video src="./e0ab60c2d692246aff03211b457506f1.mp4" controls></video>
<div><button onclick="togglePictureInPicture('')">切换画中画</button></div>
</div>
<script>
// 切换画中画方法
function togglePictureInPicture() {
const video = document.querySelector('video');
console.log('togglePictureInPicture');
if (document.pictureInPictureElement) {
document.exitPictureInPicture();
} else {
if (document.pictureInPictureEnabled) {
video.requestPictureInPicture();
}
}
}
</script>
</body>
注意 :调用video.requestPictureInPicture()
方法必须在监听用户手势操作的回调方法中执行才会生效。不能在用户不知情的情况下调用,如视频窗口在页面上不可见时自动开启小窗模式,这个API
是无法实现 的!如果这样做你将得到以下错误:
交叉观察器API
IntersectionObserver交叉观察器 接口提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根(root)。
下面是一个简单使用交叉观察器API
的小case
:
js
const video = document.getElementById('video');
const intersectionObserver = new IntersectionObserver((entries) => {
// 如果intersectionRatio属性小于或等于0,则目标在视野外
if (entries[0].intersectionRatio <= 0) {
console.log('video在视野之外')
} else {
console.log("video在视野之内");
}
});
// 开始监听
intersectionObserver.observe(video);
不管是浏览器滚动条 或是页面元素滚动条 滚动到所观察的元素不可见 的位置,简单地说就说元素从窗口消失了 都可以使用该API
观察到。
浏览器滚动条

页面元素滚动条

学会交叉观察器 API
的用法后,后面实现模拟 自动开启画中画小窗播放视频那也就信手拈来啦。
模拟b站自动小窗视频播放实战
由于前面的画中画 API
只能在监听用户的手势操作回调函数当中触发,无法实现在交叉观察器的回调函数 当中触发,所以为了实现更灵活的目的,可以通过定位功能,模拟一个画中画窗口,但灵活的代价是只能在页面当中进行观看,模拟窗口无法像原生画中画API
那样脱离浏览器窗口依然还能继续观看视频。
实现代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>画中画小案例</title>
<style>
:root {
--p-in-p-width: 1000px;
--p-in-p-height: 500px;
}
.video-container {
width: var(--p-in-p-width);
height: var(--p-in-p-height);
;
}
.video,
.mini-video {
width: 100%;
height: 100%;
}
.mini-container {
width: calc(var(--p-in-p-width) / 3);
height: calc(var(--p-in-p-height) / 3);
}
.p-in-p {
display: none;
position: fixed;
bottom: 50px;
right: 50px;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9999;
}
</style>
</head>
<body style="height: 2000px;">
<div class="video-container">
<video class="video" src="./e0ab60c2d692246aff03211b457506f1.mp4" controls></video>
</div>
<div class="p-in-p">
<div>画中画小窗</div>
<div class="mini-container">
</div>
<div><button class="closeBtn">关闭画中画</button></div>
</div>
<script>
window.onload = function () {
const videoContainer = document.querySelector('.video-container');
const video = document.querySelector('.video');
const miniVideoContainer = document.querySelector('.mini-container');
const PInPWindow = document.querySelector('.p-in-p');
const intersectionObserver = new IntersectionObserver((entries) => {
// 如果intersectionRatio属性小于或等于0,则目标在视野外
if (entries[0].intersectionRatio <= 0) {
console.log('video在视野之外')
miniVideoContainer.appendChild(video);
PInPWindow.style.display = 'block';
} else {
console.log("video在视野之内");
videoContainer.appendChild(video);
PInPWindow.style.display = 'none';
}
});
// 开始监听
intersectionObserver.observe(videoContainer);
// 手动关闭画中画
const closeBtn = document.querySelector('.closeBtn');
closeBtn.addEventListener('click', () => {
console.log('togglePictureInPicture');
PInPWindow.style.display = 'none';
});
// 页面卸载监听
window.onunload = function () {
// 终止所有监听
intersectionObserver.disconnect();
}
}
</script>
</body>
</html>
实现原理
当视频播放的主窗口隐藏后,把主窗口的video
标签移动到小窗口上继续播放,当主播放窗口出现时,把video
标签放回原来位置,再隐藏小窗。
实现效果动图

总结
首先了解了浏览器原生视频画中画 API
的用法,发现无法满足在隐式状态下自动进入视频画中画小窗播放需求。
其次学了交叉观察器 的用法,为后续手动模拟实现一个类似b站 的主窗口播放 隐藏自动进入小窗播放的效果。
最后通过一个简单实战案例 ,成功模拟了b站的主窗口播放隐藏自动进入小窗播放的效果。