html5视频水印需用canvas实时绘制:监听播放事件,通过requestanimationframe每帧drawimage视频帧并filltext添加文字,同步canvas尺寸至video.clientwidth/clientheight,设globalalpha=0.3防干扰,注意ios字体渲染与性能优化。

用 canvas 实时绘制文字水印到视频画面上
HTML5 本身不支持直接给 <video></video> 添加文字水印,必须借助 canvas 捕获帧并叠加。核心思路是监听 timeupdate 或用 requestAnimationFrame 定期将视频当前帧绘制到 canvas,再用 fillText() 写文字。
- 水印文字需在每次重绘时手动绘制,不能只画一次
- 推荐用
requestAnimationFrame而非setInterval,避免帧率抖动和丢帧 - 注意 canvas 尺寸必须与视频显示尺寸一致(不是原始分辨率),否则水印错位;可用
video.videoWidth/video.videoHeight获取原始尺寸,但绘制时应按video.clientWidth/video.clientHeight缩放计算坐标 - 文字透明度靠
ctx.globalAlpha控制,建议设为0.2–0.4,太浓影响观看,太淡易被裁掉
<video id="myVideo" src="demo.mp4" controls></video>
<canvas id="watermarkCanvas"></canvas>
<p><script>
const video = document.getElementById('myVideo');
const canvas = document.getElementById('watermarkCanvas');
const ctx = canvas.getContext('2d');</p><p>// 同步 canvas 显示尺寸
const resizeCanvas = () => {
canvas.width = video.clientWidth;
canvas.height = video.clientHeight;
};
resizeCanvas();
window.addEventListener('resize', resizeCanvas);</p><p>const drawWatermark = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);</p><p>ctx.globalAlpha = 0.3;
ctx.font = 'bold 24px sans-serif';
ctx.fillStyle = '#fff';
ctx.textAlign = 'right';
ctx.textBaseline = 'bottom';
ctx.fillText('CONFIDENTIAL', canvas.width - 20, canvas.height - 20);
ctx.globalAlpha = 1;
};</p><p>const render = () => {
if (!video.paused && !video.ended) {
drawWatermark();
}
requestAnimationFrame(render);
};
video.addEventListener('play', render);
</script></p>避免水印被右键保存或录屏绕过的关键限制
纯前端水印本质是“视觉欺骗”,无法阻止用户截图、录屏或通过 DevTools 提取原始视频流。所有叠加逻辑都在浏览器内存中运行,canvas 输出的仍是可复制图像。
- 禁用右键菜单(
oncontextmenu="return false")只能防误触,不影响开发者工具或系统级录屏 - 不要依赖
controlsList="nodownload"阻止下载——它仅隐藏下载按钮,资源 URL 仍可在 Network 面板找到 - 真正防泄漏需服务端配合:如 HLS 分片加密、动态 token 鉴权、或使用 DRM(Widevine/PlayReady),但这些与前端水印无关
- 若只需防普通用户“顺手截屏”,文字水印+低透明度+倾斜排布(用
ctx.rotate())能显著提升识别成本
移动端 Safari 上文字模糊或不显示的修复方法
iOS Safari 对 canvas 文字渲染有特殊限制:当 canvas 未显式设置 width/height 属性(而非仅 CSS 设置),或字体加载未完成时,fillText() 可能失效或糊成一团。
- 务必用 JS 设置
canvas.width和canvas.height(像素值),不能只靠 CSS 缩放 - 避免使用未声明的字体名,优先用系统安全字体如
'-apple-system, BlinkMacSystemFont, sans-serif' - 若需自定义字体,用
@font-face加载后,调用document.fonts.load()确保就绪再开始绘制 - 部分 iOS 版本对
globalAlpha+fillText组合有渲染 bug,可改用带阴影的文字:ctx.shadowColor = 'rgba(0,0,0,0.5)'; ctx.shadowBlur = 4;
性能敏感场景下减少重绘开销的优化点
高频调用 drawImage() + fillText() 在低端设备或高分辨率视频上容易掉帧,尤其当视频宽高超过 1280×720。
立即学习“前端免费学习笔记(深入)”;
- 不必每帧都画水印:可降频至每 200–300ms 绘制一次(用
performance.now()记录上次绘制时间) - 水印区域小且固定时,可预先画好一张含水印的透明 PNG,用
ctx.drawImage(watermarkImg, x, y)贴图,比实时文字快 3–5 倍 - 关闭抗锯齿:
ctx.imageSmoothingEnabled = false,对文字影响不大,但提升 canvas 绘制速度 - 如果视频暂停,停止
requestAnimationFrame循环;播放时再恢复,避免后台空转耗电
实际项目里最容易被忽略的是 canvas 尺寸同步时机——很多同学在页面加载完就设死 canvas 大小,却没监听 video 的 loadedmetadata 和窗口 resize,导致水印在视频刚加载或横竖屏切换时严重偏移。











