video元素无法防投屏,唯一可靠方案是用canvas实时绘制水印:需监听play/timeupdate/seeked事件,用requestanimationframe对齐渲染帧率,canvas尺寸同步video.clientwidth/clientheight,globalalpha设0.25,字体用sans-serif,字号≥18px,并确保视频同域或cors开启。

video 元素本身不支持防投屏
浏览器的 <video></video> 标签没有任何 API 或属性能阻止用户投屏、录屏或截图。所谓“防投屏”,实际只能靠增加盗用成本——透明水印就是其中最务实的一环:它不阻止行为,但让录下来的画面自带可追溯标识。
关键认知是:水印必须出现在投屏/录屏捕获到的帧里,而不能只靠 CSS 层叠(比如用 position: fixed 的 div 盖在 video 上),因为这类图层通常不会被系统级录屏工具捕获。
所以唯一可靠路径是——把水印画进视频帧本身,也就是用 canvas 实时绘制。
requestAnimationFrame 是绘制水印的底线要求
用 setInterval 定期重绘会导致帧率抖动、丢帧,尤其在低端设备或页面后台运行时。一旦掉帧,水印就断续,录屏时可能漏掉关键帧。
requestAnimationFrame 能与浏览器渲染节奏对齐,保证每帧都尝试绘制(即使跳过,也是有策略的跳过)。
- 必须在
video.play()后启动,否则初始帧可能为空白 - 需监听
timeupdate+play+seeked多个事件,防止拖拽后水印错位或消失 - 如果视频暂停,应停止
requestAnimationFrame循环,避免空转耗电
canvas 尺寸必须同步 video.clientWidth/clientHeight
很多人直接用 video.videoWidth/video.videoHeight 设置 canvas,结果水印严重偏移。这是因为原始分辨率 ≠ 显示尺寸 —— 视频可能被 object-fit: contain 缩放、被 CSS 限制宽高、或响应式布局拉伸。
正确做法是:
- 用
video.clientWidth和video.clientHeight获取当前显示区域尺寸 - 每次 resize 事件触发时调用
canvas.width = video.clientWidth等重设 - 绘制时
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)必须匹配该尺寸
否则文字坐标(如右下角 canvas.width - 20)会算错,iOS 上尤其明显。
globalAlpha 和字体选择直接影响识别率
透明度不是越低越好。实测 ctx.globalAlpha = 0.25 是平衡点:太淡(0.4)又容易被一键去水印工具批量抹除。
字体要避开系统缺失风险:
- 禁用
'SimHei'、'Microsoft Yahei'等中文专有字体,iOS/macOS 可能回退成方块 - 用
'sans-serif'或'system-ui',确保跨平台一致渲染 - 字号建议 ≥18px,小字号在 1080p 投屏后易糊,且 OCR 识别率骤降
最后提醒一句:所有 canvas 绘制逻辑必须放在同域或开启 CORS 的视频源上,否则 ctx.drawImage(video, ...) 会因跨域污染 canvas 而静默失败——错误不会抛出,但水印永远不出现。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











