最近在项目中用到了
Ant Design Vue水印,记录一下用法以便后期查询
以下内容转自 《Ant Design Vue - Watermark 水印》
基础用法很简单,套用在代码中即可
<a-watermark content="水印内容"></a-watermark>通过 content 设置 字符串数组 指定多行文字水印内容
<a-watermark :content="['第一行水印内容', '第二行水印内容']"></a-watermark>通过 image 指定图片地址,需设置 width 和 height,并上传至少两倍的宽高的 logo 图片地址,以防图片被拉伸
<a-watermark image="图片地址"></a-watermark>也可通过配置自定义参数实现更多水印效果
<a-watermark v-bind="model"></a-watermark>
<script lang="ts" setup>
import { reactive } from 'vue';
const model = reactive({
content: '水印内容',
font: { // 文字样式
color: 'string', // 字体颜色
fontSize: 'number', // 字体大小
fontWeight: 'normal | light | weight | number', // 字体粗细
fontFamily: 'string', // 字体类型
fontStyle: 'none | normal | italic | oblique', // 字体样式
},
zIndex: 11,
rotate: -22,
gap: [number, number], // 水印之间的间距
offset: [number, number], // 水印距离容器左上角的偏移量,默认为 gap/2
});
</script>实践到此,突然对水印的实现原理产生了困惑,作为一个图层遮罩在全屏,却不影响诸如下拉、表单等元素的交互操作
故有了以下答案
本部分内容转自《前端如何实现水印功能》
实现方式(css + 定位):
pointer-events: none 是一个 CSS 属性,用于控制元素是否可以成为鼠标事件的目标,设置后用户无法与该元素进行交互,包括点击、悬停、拖动等
具体效果
- 点击事件:用户无法点击该元素
- 悬停事件:鼠标悬停在该元素上时,不会触发任何悬停效果(如改变鼠标指针形状、显示提示信息等)
- 拖动事件:用户无法拖动该元素
- 其他事件:所有与鼠标相关的事件(如
mousedown、mouseup、mousemove等)都不会在该元素上触发
使用场景
pointer-events: none 通常用于以下场景:
- 禁用交互:当需要临时禁用某个元素的交互功能时,可以使用
pointer-events: none - 覆盖层:在某些情况下,可能需要在页面上覆盖一层半透明的元素,但又不希望该层影响底层元素的交互,这时可以使用
pointer-events: none - 视觉效果:在某些视觉效果中,可能需要显示一个元素但不希望用户与其交互(如:水印)
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.watermark {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999;
// 属性用于禁用元素的鼠标事件,这意味着用户无法与该元素进行交互(如点击、悬停等)
pointer-events: none; /* 防止水印干扰用户交互 */
background-repeat: repeat;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200"><text x="50%" y="50%" font-family="Arial" font-size="20" fill="rgba(0, 0, 0, 0.1)" text-anchor="middle" dominant-baseline="middle" transform="rotate(-45, 100, 100)">水印文本</text></svg>'); // 采用固定图片水印
}
</style>
</head>
<body>
<div class="watermark"></div>
</body>
</html>通过 JavaScript 动态生成水印,可以更灵活地控制水印内容和样式
<script>
function createWatermark(text) {
const watermarkContainer = document.getElementById('watermarkContainer');
// 使用 document.createElementNS 创建一个 SVG 元素
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
// 设置宽高为200x200,并定义 viewBox 以控制缩放比例
svg.setAttribute('width', '200');
svg.setAttribute('height', '200');
svg.setAttribute('viewBox', '0 0 200 200');
// 创建一个 <text> 元素,设置其位置为 SVG 中心(x='50%',y='50%')
const textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');
textElement.setAttribute('x', '50%');
textElement.setAttribute('y', '50%');
// 设置字体、大小、颜色(半透明黑色)、对齐方式(居中)
textElement.setAttribute('font-family', 'Arial');
textElement.setAttribute('font-size', '20');
textElement.setAttribute('fill', 'rgba(0, 0, 0, 0.1)');
textElement.setAttribute('text-anchor', 'middle');
textElement.setAttribute('dominant-baseline', 'middle');
// 使用 transform 属性将文本旋转 -45°,形成倾斜效果
textElement.setAttribute('transform', 'rotate(-45, 100, 100)');
textElement.textContent = text;
svg.appendChild(textElement);
// 使用XMLSerializer将SVG元素转换为字符串。使用btoa方法将字符串编码为Base64格式
const svgString = new XMLSerializer().serializeToString(svg);
// 将 Base64 字符串嵌入到 data:image/svg+xml;base64,... 中,生成图片 URL
const base64 = btoa(svgString);
const url = `data:image/svg+xml;base64,${base64}`;
// 将生成的图片 URL 设置为 watermarkContainer 的 backgroundImage 属性
watermarkContainer.style.backgroundImage = `url(${url})`;
}
createWatermark('水印文本');
</script>通过 canvas 动态生成水印
function createWatermark(text = 'Watermark') {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 设置画布尺寸
canvas.width = 400;
canvas.height = 200;
// 绘制水印
ctx.font = '20px Arial';
ctx.fillStyle = 'rgba(100, 100, 100, 0.2)';
ctx.rotate(-Math.PI / 6); // 旋转 -30 度
ctx.fillText(text, 50, 150);
// 生成 Base64 背景图
return canvas.toDataURL('image/png');
}
// 动态创建 style 标签,并将其插入到 head 标签内
const style = document.createElement('style');
style.innerHTML = `
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
pointer-events: none;
background: url(${createWatermark()}) repeat;
}
`;
document.head.appendChild(style);到此,水印的 html 结构依然是 <div class="watermark"></div> ,用户可以很容易的从控制台将它删除
为了解决这个问题还需要追加防删机制
<script>
// 创建 MutationObserver 实例
// 初始化一个 DOM 变化观察器,用于监听指定节点的变化
// 回调函数是所有变更记录的数组
const observer = new MutationObserver((mutations) => {
// 遍历所有变更记录
mutations.forEach((mutation) => {
// 仅处理有节点被移除的情况
if (mutation.removedNodes.length) {
const removed = Array.from(mutation.removedNodes); // 转换节点列表为数组
if (removed.some((node) => node.classList?.contains("watermark"))) {
// 重新生成水印并插入 body
document.body.appendChild(createWatermarkElement());
}
}
});
});
// 运行观察器
observer.observe(document.body, {
childList: true, // 监听子节点的添加 / 移除
subtree: true // 监听目标节点所有后代节点的变化
});
</script>