CSS伪元素适合静态展示,canvas更可控;伪元素用SVG水印需注意URL编码和pointer-events;canvas需处理devicePixelRatio避免模糊;响应式水印应基于naturalWidth/Height或SVG viewBox。

水印是用CSS伪元素还是canvas实现更可控
直接用 ::before 或 ::after 叠加文字水印最轻量,适合静态展示;但若要旋转、透明度精细调节、或水印随图片缩放自适应,canvas 是唯一可靠方案。CSS伪元素对背景图不生效,且无法保证水印始终覆盖在图片内容之上(尤其当图片带 object-fit: cover 时)。
常见错误:用 position: absolute 在父容器里硬塞一个 div 当水印——结果图片尺寸一变,水印就偏移或裁切。真正稳的方式是把水印“画进”图片本身,或用 canvas 动态合成。
- 纯展示页面 → 优先用
::after+background-image生成 SVG 水印(抗锯齿好、缩放无损) - 需用户上传后实时加水印 → 必须用
canvas,读取原图 → 绘制文字/Logo → 导出新Blob - 服务端已处理好带水印的图 → 前端只负责正确显示,无需额外逻辑
用CSS伪元素加SVG水印的实际写法
关键不是写个 content: "©2024" 就完事,得让水印真正“贴合”图片区域,且不干扰点击和选中。SVG 比纯文字更灵活:可设透明度、旋转角度、重复平铺。
img.watermarked {
position: relative;
display: inline-block;
}
img.watermarked::after {
content: "";
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Ctext x='50%25' y='50%25' font-size='16' fill='rgba(255,255,255,0.15)' text-anchor='middle' dominant-baseline='middle' transform='rotate(-30,100,100)'%3EYourSite.com%3C/text%3E%3C/svg%3E");
background-repeat: repeat;
pointer-events: none;
}注意点:
立即学习“前端免费学习笔记(深入)”;
-
pointer-events: none必须加,否则水印层会挡住图片的click或hover - SVG 中的
%25是 URL 编码的%,漏掉会失效 -
transform='rotate(-30,100,100)'表示绕 SVG 中心点旋转,不是绕图片中心 - 如果图片宽高比不固定,
background-size: 200px 200px比cover更可控
canvas动态绘制水印时容易忽略的像素比问题
在 Retina 屏上,canvas.width 和 canvas.style.width 不一致会导致水印模糊——这是最常被跳过的坑。你设了 canvas.width = 800,但 CSS 设成 width: 400px,浏览器自动双倍采样,文字就糊了。
正确做法:
- 先用
window.devicePixelRatio获取设备像素比 - 设置
canvas.width = 图片自然宽 * ratio,canvas.height = 图片自然高 * ratio - 再用
ctx.scale(ratio, ratio)缩放绘图上下文,这样字体大小、线条粗细才不会失真 - 最后导出时用
canvas.toDataURL('image/png', 0.9),避免 JPEG 压缩进一步劣化文字边缘
示例关键行:
const ratio = window.devicePixelRatio || 1;
canvas.width = img.naturalWidth * ratio;
canvas.height = img.naturalHeight * ratio;
const ctx = canvas.getContext('2d');
ctx.scale(ratio, ratio);
ctx.font = 'bold 24px sans-serif';
ctx.fillStyle = 'rgba(255,255,255,0.2)';
ctx.textAlign = 'center';
ctx.fillText('CONFIDENTIAL', img.naturalWidth / 2, img.naturalHeight / 2);水印区域需要响应式?别用百分比定位文字
想让水印始终居中于图片区域,很多人写 left: 50%; top: 50%; transform: translate(-50%, -50%) ——这在固定尺寸容器里没问题,但图片用 max-width: 100% 或 object-fit 时,实际渲染尺寸和自然尺寸不一致,文字就偏了。
根本解法只有两个:
- 用
canvas:永远基于img.naturalWidth/Height计算坐标,不受 CSS 缩放影响 - 用 SVG 水印:把 SVG 的
viewBox设为"0 0 [宽] [高]",然后用background-size: contain或100% 100%铺满,它会自动等比缩放
额外提醒:如果图片区域是 flex 或 grid 容器里的子项,务必确认父容器没设 overflow: hidden 以外的裁剪行为——某些旧版 Safari 对 clip-path 和伪元素叠加有兼容问题。



















