> 技术文档 > 前端如何实现水印功能_前端页面水印

前端如何实现水印功能_前端页面水印


水印功能的核心作用

‌在前端实现水印功能是一个常见的需求,可以用于保护内容不被未经授权的复制和分发。水印可以是文本、图像或其他形式的标记,通常放置在页面的背景或内容上。

实现方式:

1. 实现文本水印

文本水印是最常见的水印形式,通常用于保护网页内容。

1.1 使用 CSS 实现文本水印(css+定位)

通过 CSS 的 ::before 或 ::after 伪元素来实现文本水印。

示例代码:

1. pointer-events: none 的作用

pointer-events: none; 是一个CSS属性,用于控制元素是否可以成为鼠标事件的目标。具体来说,当一个元素设置了 pointer-events: none; 后,该元素将不会响应任何鼠标事件,包括点击、悬停、拖动等。这意味着用户无法与该元素进行交互。

2. 具体效果

  • 点击事件:用户无法点击该元素。
  • 悬停事件:鼠标悬停在该元素上时,不会触发任何悬停效果(如改变鼠标指针形状、显示提示信息等)。
  • 拖动事件:用户无法拖动该元素。
  • 其他事件:所有与鼠标相关的事件(如 mousedownmouseupmousemove 等)都不会在该元素上触发。

3. 使用场景

pointer-events: none; 通常用于以下场景:

  • 禁用交互:当需要临时禁用某个元素的交互功能时,可以使用 pointer-events: none;
  • 覆盖层:在某些情况下,可能需要在页面上覆盖一层半透明的元素,但又不希望该层影响底层元素的交互,这时可以使用 pointer-events: none;
  • 视觉效果:在某些视觉效果中,可能需要显示一个元素但不希望用户与其交互。
  文本水印示例  body { font-family: Arial, sans-serif; margin: 0; padding: 0; height: 100vh; position: relative; } .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,水印文本\'); }  

欢迎访问我们的网站

这是带有水印的示例页面。

 

1.2 使用 JavaScript 动态生成水印

通过 JavaScript 动态生成水印,可以更灵活地控制水印内容和样式。

示例代码:

  动态文本水印示例  body { font-family: Arial, sans-serif; margin: 0; padding: 0; height: 100vh; position: relative; } .watermark { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 999; pointer-events: none; /* 防止水印干扰用户交互 */ background-repeat: repeat; }  

欢迎访问我们的网站

这是带有水印的示例页面。

function createWatermark(text) { const watermarkContainer = document.getElementById(\'watermarkContainer\');//使用document.createElementNS创建一个SVG元素。设置宽高为200x200,并定义viewBox以控制缩放比例 const svg = document.createElementNS(\'http://www.w3.org/2000/svg\', \'svg\'); svg.setAttribute(\'width\', \'200\'); svg.setAttribute(\'height\', \'200\'); svg.setAttribute(\'viewBox\', \'0 0 200 200\');//创建一个元素,设置其位置为SVG中心(x=\'50%\',y=\'50%\')。设置字体、大小、颜色(半透明黑色)、对齐方式(居中)。使用transform属性将文本旋转-45°,形成倾斜效果。 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\'); textElement.setAttribute(\'transform\', \'rotate(-45, 100, 100)\'); textElement.textContent = text; svg.appendChild(textElement); //使用XMLSerializer将SVG元素转换为字符串。使用btoa方法将字符串编码为Base64格式。将Base64字符串嵌入到data:image/svg+xml;base64,...中,生成图片URL。 const svgString = new XMLSerializer().serializeToString(svg); const base64 = btoa(svgString); const url = `data:image/svg+xml;base64,${base64}`; //将生成的图片URL设置为watermarkContainer的backgroundImage属性,从而实现动态水印效果。 watermarkContainer.style.backgroundImage = `url(${url})`; } createWatermark(\'水印文本\');
1.3 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\');}// 应用水印样式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);

 2 MutationObserver 防删水印代码

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\'))) { document.body.appendChild(createWatermarkElement()); } } });});observer.observe(document.body, { childList: true, subtree: true});

分步解析

1. 创建 MutationObserver 实例
const observer = new MutationObserver((mutations) => { ... });
  • 作用‌:初始化一个 DOM 变化观察器,用于监听指定节点的变化
  • 回调参数‌:mutations 是所有变更记录的数组

 ‌2. 遍历所有变更记录

mutations.forEach((mutation) => { ... });

 

  • mutation 对象‌:包含以下关键属性:
    • type:变更类型(如 childListattributes
    • removedNodes:被移除的节点列表(NodeList
    • addedNodes:新增的节点列表

 ‌3. 检测是否有节点被移除

if (mutation.removedNodes.length) { // 处理被删除的节点}
  • 逻辑‌:仅当有节点被移除时才进入处理

 ‌4. 转换节点列表为数组

const removed = Array.from(mutation.removedNodes);
  • 原因‌:removedNodes 是类数组对象 NodeList,转换为数组便于使用 Array.some() 方法
  • 等效代码‌:[...mutation.removedNodes]

 ‌5. 检查被删节点是否包含水印

removed.some(node => node.classList?.contains(\'watermark\'))
  • 方法‌:Array.some() 检测数组中是否有元素满足条件
  • 安全操作‌:node.classList?.contains() 使用可选链避免 null.classList 报错
  • 目标‌:确认被删除节点中存在 class=\"watermark\" 的元素

 ‌6. 重新插入水印

document.body.appendChild(createWatermarkElement());
  • 恢复逻辑‌:若水印被删除,立即调用 createWatermarkElement() 创建新水印并插入到  末尾
  • 注意‌:需提前定义 createWatermarkElement() 方法(生成水印 DOM 元素)

 ‌7. 启动观察器

observer.observe(document.body, { childList: true, subtree: true});
  • 配置项‌:
    • childList: true:监听目标节点子节点的添加/移除
    • subtree: true:监听目标节点所有后代节点的变化
  • 监控范围‌:document.body 及其所有子节点的变更

方案对比

方案‌ ‌优点‌ ‌缺点‌ CSS 绝对定位 实现简单、性能高 易被开发者工具删除 Canvas 背景图 难以直接删除、支持复杂样式 内存占用稍高 防篡改监控 增强安全性 增加性能开销 服务端生成 安全性最高 需后端配合、增加服务器负载