Watermark 水印
概述
SWatermark 在页面内容上方叠加重复的文字或图片图案,用于标识所有权、机密性或品牌。它生成平铺的 canvas data URL 作为 overlay 的 background-image,支持文字和图片水印,可配置旋转、间距、偏移和交叉图案,并提供可选的防篡改机制——当 overlay 被通过 DevTools 删除或修改时自动恢复。
提示:除了
SWatermark外,Aria 层还导出了WatermarkCompact作为默认的 root/overlay 结构,以及WatermarkRoot、WatermarkOverlay和provideWatermarkUi用于完全自定义组合和样式注入。
用法
功能
- 📝 文字水印 — 渲染重复文字,可配置字号、字体、颜色和粗细。
- 🖼️ 图片水印 — 传入
imageURL 平铺图片图案;支持crossOrigin: 'anonymous'处理 CORS 源。 - 🔄 旋转 — 任意角度旋转水印(默认
-22°)。 - 📐 间距与偏移 — 通过
gap: [x, y]控制平铺间距,offset: [x, y]控制定位。 - ✖️ 交叉图案 — 设
cross: true沿两条对角线渲染两个重叠水印。 - 🛡️ 防篡改 — 当
defense: true时,MutationObserver检测 overlay 删除或属性篡改,自动重新渲染 overlay。 - 🖥️ 全屏模式 — 设
fullscreen: true将 overlay 固定到视口而非父容器。 - 🔧 Aria 组合 — 导出
WatermarkRoot+WatermarkOverlay+WatermarkCompact用于自定义布局;通过provideWatermarkUi注入样式。 - 🌐 SSR 安全 — Canvas 生成由
typeof window === 'undefined'守护;服务端不渲染 overlay。
示例
Basic
This content is protected by a repeating text watermark.
Cross
Cross pattern creates a denser watermark with two overlapping diagonal layers.
Fullscreen
Fullscreen mode positions the watermark overlay fixed to the viewport, so it remains visible even when content scrolls.
API
Aria 层
4Watermark
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
class | ClassValue | — | 根元素的 class 属性。 |
ui | Partial<WatermarkUi> | — | 每个插槽的 UI 类重写。 |
fullscreen | boolean | false | 当值为 true 时,水印叠加层将固定在视口内,而非父容器内。 |
overlayProps | WatermarkOverlayProps | — | 转发给覆盖元素的属性。 |
content | string | — | 水印的文本内容。 |
image | string | — | 水印的图片 URL。
当同时提供内容和图片时,以图片为准。 |
fontSize | number | 16 | 水印文本的字号(单位:像素)。 |
fontFamily | string | 'sans-serif' | 水印文本的字体家族。 |
fontColor | string | 'rgba(0, 0, 0, 0.15)' | 水印文本的字体颜色。 |
fontWeight | string | number | 'normal' | 水印文本的字重。 |
rotate | number | -22 | 水印的旋转角度(单位:度)。 |
gap | [number, number] | [100, 100] | 水印图块之间的间距(以像素为单位),表示为 [x, y]。 |
offset | [number, number] | [0, 0] | 每个图块内水印的偏移量(以像素为单位),表示为 [x, y]。 |
width | number | — | 单个水印图块的宽度(以像素为单位)。
若未提供该值,系统将根据文本尺寸和间距自动计算。 |
height | number | — | 单个水印图块的高度(以像素为单位)。
若未提供该值,系统将根据文本尺寸和间距自动计算。 |
cross | boolean | false | 当值为 true 时,渲染一个由两个重叠水印组成的对角线十字图案。 |
defense | boolean | false | 当值为 true 时,若水印叠加层遭到篡改或移除,则会对其进行监控并恢复。 |
WatermarkCompact
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
overlayProps | WatermarkOverlayProps | — | 转发给覆盖元素的属性。 |
content | string | — | 水印的文本内容。 |
image | string | — | 水印的图片 URL。
当同时提供内容和图片时,以图片为准。 |
fontSize | number | 16 | 水印文本的字号(单位:像素)。 |
fontFamily | string | 'sans-serif' | 水印文本的字体家族。 |
fontColor | string | 'rgba(0, 0, 0, 0.15)' | 水印文本的字体颜色。 |
fontWeight | string | number | 'normal' | 水印文本的字重。 |
rotate | number | -22 | 水印的旋转角度(单位:度)。 |
gap | [number, number] | [100, 100] | 水印图块之间的间距(以像素为单位),表示为 [x, y]。 |
offset | [number, number] | [0, 0] | 每个图块内水印的偏移量(以像素为单位),表示为 [x, y]。 |
width | number | — | 单个水印图块的宽度(以像素为单位)。
若未提供该值,系统将根据文本尺寸和间距自动计算。 |
height | number | — | 单个水印图块的高度(以像素为单位)。
若未提供该值,系统将根据文本尺寸和间距自动计算。 |
cross | boolean | false | 当值为 true 时,渲染一个由两个重叠水印组成的对角线十字图案。 |
defense | boolean | false | 当值为 true 时,若水印叠加层遭到篡改或移除,则会对其进行监控并恢复。 |
WatermarkOverlay
WatermarkRoot
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
content | string | — | 水印的文本内容。 |
image | string | — | 水印的图片 URL。
当同时提供内容和图片时,以图片为准。 |
fontSize | number | 16 | 水印文本的字号(单位:像素)。 |
fontFamily | string | 'sans-serif' | 水印文本的字体家族。 |
fontColor | string | 'rgba(0, 0, 0, 0.15)' | 水印文本的字体颜色。 |
fontWeight | string | number | 'normal' | 水印文本的字重。 |
rotate | number | -22 | 水印的旋转角度(单位:度)。 |
gap | [number, number] | [100, 100] | 水印图块之间的间距(以像素为单位),表示为 [x, y]。 |
offset | [number, number] | [0, 0] | 每个图块内水印的偏移量(以像素为单位),表示为 [x, y]。 |
width | number | — | 单个水印图块的宽度(以像素为单位)。
若未提供该值,系统将根据文本尺寸和间距自动计算。 |
height | number | — | 单个水印图块的高度(以像素为单位)。
若未提供该值,系统将根据文本尺寸和间距自动计算。 |
cross | boolean | false | 当值为 true 时,渲染一个由两个重叠水印组成的对角线十字图案。 |
defense | boolean | false | 当值为 true 时,若水印叠加层遭到篡改或移除,则会对其进行监控并恢复。 |
注意事项
架构与对标差异
SWatermark 拆分为 Aria 层(负责 canvas 生成、overlay 状态和防篡改逻辑)和 styled 层(负责 watermarkVariants scv() 配方,root: relative,overlay: absolute inset-0 pointer-events-none bg-repeat)。Aria WatermarkCompact 组合 WatermarkRoot + WatermarkOverlay,并提供 repairOverlay 函数用于防篡改。
| 维度 | VeanUI | Ant Design Watermark | Element Plus Watermark | MUI Watermark |
|---|---|---|---|---|
| 架构 | Aria + styled 分离 | 仅 styled | 仅 styled | 仅 styled |
| 文字水印 | ✅ | ✅ | ✅ | ✅ |
| 图片水印 | ✅ crossOrigin: anonymous | ✅ | ✅ | — |
| 交叉图案 | ✅ cross prop | — | — | — |
| 防篡改 | ✅ MutationObserver(删除 + 属性) | ✅ MutationObserver | ✅ MutationObserver | — |
| 全屏模式 | ✅ fullscreen prop | ✅ inherit | ✅ content slot | — |
| Aria 导出 | ✅ Root + Overlay + Compact | — | — | — |
| gap/offset 控制 | ✅ gap + offset | ✅ gap + offset | ✅ gap + offset | ✅ gap |
运行时注意事项
- Canvas 依赖:水印生成使用
<canvas>和canvas.toDataURL()。在不支持 canvas 的环境(如某些 SSR 设置)中,overlay 不会渲染——generateWatermarkDataUrl返回undefined。 - 图片 CORS:图片水印使用
crossOrigin = 'anonymous'。图片服务器必须发送适当的 CORS 头(Access-Control-Allow-Origin),否则 canvas 会被污染,toDataURL()会抛错。 - 防篡改默认关闭:
defenseprop 默认false。设defense: true启用基于MutationObserver的篡改检测。防篡固有较小的性能开销(observer 回调)。 - 防篡改范围:防篡改机制检测 overlay 删除(通过 root 的
childListobserver)和属性篡改(通过 overlay 的attributesobserver)。检查aria-hidden、class、style、hidden和data-vean-watermark-overlay属性。如发现修改,通过:key递增重新渲染 overlay。 fullscreen定位:当fullscreen: true时,overlay 从absolute inset-0(相对父容器)切换到fixed inset-0 z-9999(固定视口),覆盖整个屏幕。
Aria 组合
如果默认的 root/overlay 结构足够使用,可以从 @vean/aria/watermark 导入 WatermarkCompact。如果需要独立控制 root 和 overlay 元素,可以直接组合 Aria 原语:
<script setup lang="ts">
import { computed } from 'vue';
import { WatermarkOverlay, WatermarkRoot, provideWatermarkUi } from '@vean/aria';
const ui = computed(() => ({
root: 'relative',
overlay: 'absolute inset-0 pointer-events-none bg-repeat'
}));
provideWatermarkUi(ui);
</script>
<template>
<WatermarkRoot content="机密" :rotate="-22">
<slot />
<WatermarkOverlay />
</WatermarkRoot>
</template>常见问题
如何给整个页面加水印? 设 fullscreen: true:<SWatermark content="机密" fullscreen />。overlay 切换到 fixed inset-0 z-9999,覆盖视口。
如何防止用户通过 DevTools 删除水印? 启用防篡改:<SWatermark content="机密" defense />。组件使用 MutationObserver 检测 overlay 删除或属性篡改,自动重新渲染 overlay。注意这不是万无一失的——有决心的用户仍可禁用 JavaScript 或阻止 observer。
可以使用图片作为水印图案吗? 可以。传入 image prop:<SWatermark image="/logo.png" />。图片使用 crossOrigin = 'anonymous' 加载,因此服务器必须发送 CORS 头。当 content 和 image 同时提供时,image 优先。
cross 交叉图案是什么? 设 cross: true 渲染两个重叠水印——一个沿主对角线,一个沿反对角线(反向旋转)。这会创建更密集的交叉图案。
如何控制平铺间距? 使用 gap prop:<SWatermark content="草稿" :gap="[200, 150]" />。第一个值是水平间距,第二个是垂直间距。使用 offset 在每个平铺块内偏移水印。