前端如何实现水印功能

水印功能的核心作用

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

实现方式:

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;
  • 视觉效果:在某些视觉效果中,可能需要显示一个元素但不希望用户与其交互。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文本水印示例</title>
    <style>
        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,<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>
    <h1>欢迎访问我们的网站</h1>
    <p>这是带有水印的示例页面。</p>
</body>
</html>

 

1.2 使用 JavaScript 动态生成水印

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

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>动态文本水印示例</title>
    <style>
        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;
        }
    </style>
</head>
<body>
    <div id="watermarkContainer"></div>
    <h1>欢迎访问我们的网站</h1>
    <p>这是带有水印的示例页面。</p>
    <script>
        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');
//创建一个<text>元素,设置其位置为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('水印文本');
    </script>
</body>
</html>
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() 创建新水印并插入到 <body> 末尾
  • 注意‌:需提前定义 createWatermarkElement() 方法(生成水印 DOM 元素)

 ‌7. 启动观察器

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

方案对比

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值