结论:CSS水印更轻量、兼容性好,但防截屏能力弱;canvas水印能覆盖滚动区域、动态生成,但性能稍差、iOS可能被系统截图绕过。
canvas 还是 CSS?直接说结论:CSS 水印更轻量、兼容性好,但防截屏能力弱;canvas 水印能覆盖滚动区域、动态生成,但性能稍差、iOS 上可能被系统截图绕过。真实项目里,90% 的“防截屏”需求其实只是防误传,用 CSS 就够了——别一上来就写 canvas。
常见错误现象:background-image 水印在 iOS 微信里不显示、水印文字被页面内容遮挡、下拉刷新后水印错位。
canvas 适合需要动态拼接用户信息(如「张三-20240520」)或水印必须随滚动始终可见的场景page 层级渲染顺序:CSS 水印要挂载在 <page> 根节点上,不能只加在某个 <view> 内不是简单加个 background,关键在「覆盖全屏 + 不干扰交互 + 适配多端」。
常见错误现象:水印只盖住内容区、安卓正常 iOS 白屏、水印文字模糊、微信开发者工具里正常真机失效。
::before 伪元素实现,避免额外 DOM 节点影响布局和生命周期position: fixed 和 z-index: 9999,且父容器不能有 overflow: hidden
vw 单位(比如 font-size: 6vw),比 rem 更稳,避免 iPhone 小屏文字过小opacity: 0.08 左右,太浅看不见,太深影响阅读-webkit-transform: rotate(-12deg),否则旋转水印会锯齿不是代码写错了,而是生命周期和渲染时机没对上。canvas 水印必须等页面真正渲染完成、尺寸确定后再绘制,否则画布为空或尺寸为 0。
常见错误现象:onLoad 里直接 draw 报错、水印只出现在首屏、切换 tab 后水印消失、H5 端正常小程序白屏。
onLoad 或 onShow 里立即调用 uni.createCanvasContext,改用 $nextTick 或 setTimeout(..., 0)
resize 事件重绘(尤其分屏/横竖屏切换),小程序端用 uni.onWindowResize
getContext('2d'),小程序用 uni.createCanvasContext('id', this),ID 必须唯一且在模板中声明drawImage 复用得说清楚:没有绝对防截屏的前端方案。所谓“防”,只是提高截图成本和降低传播意愿。
容易被忽略的地方:
cover-view 或 video 组件,CSS 水印会被遮挡,必须用 canvas 叠加在同层渲染器上复杂点在于:你得先想清楚水印到底防谁——防同事随手转发?还是防恶意批量抓取?前者 CSS 足够,后者得配合服务端签名、动态 token、截图检测等后端手段。前端水印只是第一道视线屏障,不是锁。