防投屏核心依据是document.pictureInPictureElement非null,辅以screen.orientation变化;需监听enter/leavepictureinpicture事件并配合disableRemotePlayback属性与服务端动态水印。

防投屏的核心判断依据是 document.pictureInPictureElement 和 screen.orientation
浏览器本身不提供“禁止投屏”API,所谓防投屏,本质是检测用户是否已进入画中画(PiP)或投屏状态,并主动中断播放或隐藏内容。最可靠信号是 document.pictureInPictureElement:只要它非 null,说明当前有视频处于 PiP 模式(Chrome / Edge / Safari 均支持);部分安卓 Chrome 还会触发 screen.orientation 变更(如从 portrait-primary 变为 landscape-primary),可作为辅助判断。
注意:webkitDisplayingFullscreen 或 document.fullscreenElement 不能替代——它们只反映全屏,和投屏/ PiP 无关。
监听 PiP 切换需用 enterpictureinpicture 和 leavepictureinpicture 事件
直接轮询 document.pictureInPictureElement 效率低且不及时。应绑定原生事件:
const video = document.querySelector('video');
video.addEventListener('enterpictureinpicture', () => {
console.log('PiP activated');
video.pause(); // 或加载模糊遮罩、切换占位图
});
video.addEventListener('leavepictureinpicture', () => {
console.log('PiP exited');
// 可选:自动恢复播放(需用户手势触发才允许)
});
关键点:
立即学习“Java免费学习笔记(深入)”;
-
enterpictureinpicture在 PiP 窗口真正显示后触发,此时video.paused仍为false,需手动pause() -
leavepictureinpicture触发时,video已回到主页面 DOM,但播放状态不会自动恢复——浏览器策略要求后续play()必须由用户手势(如 click)触发 - 该事件仅对调用过
video.requestPictureInPicture()的视频有效;若用户通过地址栏按钮或系统快捷键进入 PiP,部分旧版 Safari 可能不触发
拦截投屏需配合 disableRemotePlayback 属性 + 后端水印
disableRemotePlayback="true" 是唯一能阻止投屏菜单出现的 HTML 属性:
<video disableRemotePlayback="true" src="video.mp4"></video>
但它仅在部分环境生效:
- Chrome Android:隐藏“投射”按钮,但无法阻止 Miracast / Samsung Dex 等系统级投屏
- iOS Safari:完全无效(Apple 不支持该属性)
- 桌面 Chrome:仅禁用 PiP 按钮,不影响 Cast 按钮
因此纯前端防投屏必然失效。真实场景中必须叠加服务端动态水印(如含用户 ID 的半透明浮层),并确保水印帧与视频流同步渲染(用 canvas.drawImage() + requestVideoFrameCallback),否则截图/录屏仍可绕过。
常见误判:把 visibilitychange 当作投屏信号
很多方案监听 document.visibilityState === 'hidden' 并暂停视频,但这和投屏无关——页面切到后台、标签页失焦、系统锁屏都会触发,导致正常浏览时频繁中断。更糟的是,Android 投屏时页面 visibility 通常仍是 'visible'。
真正需要关注的组合信号只有两个:
-
document.pictureInPictureElement !== null(主判断) - 搭配
screen.orientation.type.includes('landscape')且document.hasFocus() === false(辅助排除旋转屏幕误判)
没有银弹。PiP 和投屏行为高度依赖 UA 和系统版本,iOS 上几乎无法可靠拦截,优先保障水印不可擦除比前端拦截更实际。



















