全屏/投屏时水印消失的根本原因是浏览器原生播放器接管渲染层,DOM叠加层被剥离;防录屏滚动水印必须画入视频帧,用canvas实时绘制,并配合requestAnimationFrame与timeupdate双监听、动态缩放字体、严格边界控制及含动态字段的低透明度文字。

video 全屏/投屏时水印消失的根本原因
直接用 div 叠在 <video> 上、或用 CSS 伪元素加文字,全屏后就没了——不是“隐藏”,而是浏览器原生全屏播放器会接管渲染层,DOM 叠加层被完全剥离。投屏(如 Chrome Cast、AirPlay)同理:画面是从 video 解码帧直接推流,不经过页面 DOM 树。所以防投屏水印必须是“画进视频帧里”的,只能靠 canvas 实时绘制。
滚动水印必须用 requestAnimationFrame + timeupdate 双监听
只监听 timeupdate 不够:它触发频率不稳定(尤其低帧率视频或卡顿时),会导致文字跳变或卡顿;只靠 requestAnimationFrame 又可能在视频暂停时还在空转。真实场景必须两者配合:
-
play和playing事件触发首次绘制,并启动requestAnimationFrame循环 -
timeupdate用于更新时间戳类水印内容(如当前播放时间、用户 ID + 时间组合) - 每次绘制前检查
!video.paused && !video.ended,避免无效调用 - 滚动逻辑不能依赖
video.currentTime线性偏移(因 seek 或缓冲会跳变),应基于performance.now()或自增帧计数器
滚动位置怎么算才不跑出画布
关键不是“动起来”,而是动得稳、不闪、不越界。右下角静态水印好写,滚动难在边界处理:
- 文字起始
x坐标建议用canvas.width + 200(从右侧外开始),再每帧减固定像素(如-1.5),到-textWidth - 20时重置回起点 - 避免用
video.videoWidth/video.videoHeight——那是原始分辨率,全屏时 canvas 显示尺寸是video.clientWidth/video.clientHeight,必须用后者 - 移动端横竖屏切换时,
resize事件可能延迟,需在绘制函数内加if (canvas.width !== video.clientWidth)强制重设尺寸 - 字体大小别写死
24px,按 canvas 宽度动态缩放(如Math.max(16, Math.floor(canvas.width / 40)))
防录屏滚动水印的三个硬约束
真要让录屏软件也捕获到滚动文字,以下三点漏一个,水印就形同虚设:
立即学习“前端免费学习笔记(深入)”;
- canvas 必须和 video 同层级、且
z-index高于所有 UI 控件(包括自定义播放器按钮),否则录屏只抓到控件层下面的空白 -
ctx.globalAlpha = 0.25是安全上限:低于 0.2 容易被降噪算法抹掉;高于 0.3 在暗场视频中太刺眼 - 文字内容必须含动态字段(如
${user.id}-${Date.now() % 1000}),否则录屏工具可帧差识别并批量擦除重复图案
滚动本身不是目的,是增加帧间差异性——静态水印容易被 AI 去水印模型泛化识别,而带位移+时间戳的文本,每帧都是新样本。



















