body::before 是最轻量的文字水印方案,不增 DOM、不干扰滚动与点击,但需设 pointer-events: none 和 z-index: -1;content 插入文字,transform 旋转 -30deg,opacity: 0.08;background-image 铺 PNG 防截图,须用 absolute/fixed 定位、fixed attachment、200×200px 以上带 alpha 图片;个性化水印需 Canvas 动态绘制,注意字体、尺寸及异步触发;打印时水印默认不显示,需单独配置 @media print。

直接用 body::before 加文字水印最轻量
静态页不需要 JS,body::before 是首选:它不增加 DOM 节点、不干扰滚动、不响应点击,也不影响打印(注意:这点是双刃剑,如果要导出带水印的 PDF 就得换方案)。关键是要控制好层级和穿透性。
常见错误是忘记设 pointer-events: none,导致水印层挡住按钮或链接;或者漏掉 z-index: -1,结果水印浮在内容上面,遮字又误触。
- 用
content: "内部资料"插入文字,别用空格或 模拟缩进 -
transform: rotate(-30deg)倾斜,角度太小(如 -15deg)易被忽略,太大(-45deg)影响阅读节奏 - 透明度用
opacity: 0.08,不是 0.8——后者会严重干扰文字辨识 - 必须加
position: fixed或absolute,否则伪元素会随文档流塌陷
background-image 铺 PNG 水印最防截图
截图工具(如 Snipaste、微信截图)通常只捕获 viewport 内的渲染像素,而 background-image 是 CSS 渲染层的一部分,不会被单独剥离。但前提是水印图本身带 alpha 通道,不能靠 opacity 整体压暗背景——那会连渐变、阴影一起变淡。
容易踩的坑是滚动时水印“滑走”,尤其在移动端 Safari 或 Chrome 滚动惯性强的场景。根本解法不是调 background-attachment: scroll,而是明确写 background-attachment: fixed。
立即学习“前端免费学习笔记(深入)”;
- 图片尺寸建议 200×200px 起,太小平铺后锯齿明显;太大则单个水印块突兀
-
background-repeat: repeat是默认行为,但显式写出更稳妥,避免被重置 - 路径别用相对路径如
./watermark.png,优先用绝对路径或 base64 内联,防 404 导致空白背景 - 慎用
background-size: contain——它会让水印缩成一团居中,失去覆盖感
Canvas 动态生成水印适合带用户信息的场景
当水印需含当前登录人姓名、时间戳或权限等级(比如 “张三|2026-09-03|仅限查看”),就必须用 Canvas 动态绘制。静态图做不到个性化,而伪元素 content 又无法读取 JS 变量。
性能上要注意:Canvas 绘制 + toDataURL 是同步阻塞操作,若水印文字多、字体复杂,首屏会卡顿。别在 DOMContentLoaded 里直接执行,改用 requestIdleCallback 或延后到交互后触发。
- 字体选系统安全字体,如
"Microsoft Yahei", sans-serif,避免 fallback 失败导致文字不显示 - 倾斜角度统一用
-Math.PI / 6(即 -30°),别混用度数和弧度 - 生成的 base64 字符串直接赋给
body.style.backgroundImage,别再套一层url()函数——那是重复包装 - 记得设
canvas.width和canvas.height,否则绘图区域为 0×0,输出为空白图
透明水印叠加要避开 z-index 和 pointer-events 的坑
所谓“透明叠加”,本质是两层:底层是内容,上层是半透水印。但很多人误以为只要 opacity 低就万事大吉,忽略了层叠上下文(stacking context)的创建条件。比如父容器用了 transform 或 will-change,就会隔离子元素的 z-index,导致水印层永远沉底。
另一个高频问题是水印层拦截鼠标事件。即使透明度为 0.05,只要没设 pointer-events: none,所有点击、hover 都会被它吞掉——用户点不到按钮,也划不了滚动条。
- 水印容器必须有
position: absolute或fixed,且父容器设position: relative或已存在 stacking context -
z-index值不用很大,z-index: 1足够,但必须比内容层高(内容层默认是 auto,等价于 0) - 移动端要额外测试
touch-action: none,否则 iOS 上滑动可能失效 - 别在水印层上加
user-select: none——这解决不了问题,反而让开发者调试时无法选中它
::before 或 background-image 实现的水印,默认都不会出现在浏览器「打印预览」里。如果页面需要导出带水印的 PDF,必须另起一套 @media print 规则,或改用 Canvas 合成整页图像。



















