当前位置:首页 > React

如何实现一个react水印组件

2026-01-26 04:34:01React

实现React水印组件的方法

使用Canvas绘制水印

通过Canvas动态生成水印图案,可以灵活控制水印的样式和位置。

如何实现一个react水印组件

import React, { useEffect, useRef } from 'react';

const Watermark = ({ text, fontSize, color, opacity, angle }) => {
  const canvasRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');

    // 设置canvas尺寸为父容器大小
    canvas.width = canvas.parentElement.clientWidth;
    canvas.height = canvas.parentElement.clientHeight;

    // 清除画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 设置水印样式
    ctx.font = `${fontSize}px Arial`;
    ctx.fillStyle = color;
    ctx.globalAlpha = opacity;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';

    // 计算水印间距
    const spacingX = canvas.width / 4;
    const spacingY = canvas.height / 4;

    // 绘制水印
    for (let x = 0; x < canvas.width; x += spacingX) {
      for (let y = 0; y < canvas.height; y += spacingY) {
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate((angle * Math.PI) / 180);
        ctx.fillText(text, 0, 0);
        ctx.restore();
      }
    }
  }, [text, fontSize, color, opacity, angle]);

  return <canvas ref={canvasRef} style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }} />;
};

使用CSS背景图

通过CSS重复背景图实现简单水印效果,适合静态水印需求。

如何实现一个react水印组件

const Watermark = ({ text }) => {
  const watermarkStyle = {
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
    backgroundImage: `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}</text></svg>')`,
    backgroundRepeat: 'repeat',
    pointerEvents: 'none',
    zIndex: 9999
  };

  return <div style={watermarkStyle} />;
};

防止水印被删除

通过MutationObserver监测DOM变化,防止水印被意外删除或修改。

const ProtectedWatermark = ({ children }) => {
  const containerRef = useRef(null);

  useEffect(() => {
    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.removedNodes.length > 0) {
          const watermark = containerRef.current.querySelector('.watermark');
          if (!watermark) {
            const newWatermark = document.createElement('div');
            newWatermark.className = 'watermark';
            // 重新创建水印逻辑
            containerRef.current.appendChild(newWatermark);
          }
        }
      });
    });

    if (containerRef.current) {
      observer.observe(containerRef.current, {
        childList: true,
        subtree: true
      });
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div ref={containerRef} style={{ position: 'relative' }}>
      {children}
      <Watermark text="Confidential" />
    </div>
  );
};

完整组件示例

结合上述方法的完整水印组件实现。

import React, { useEffect, useRef } from 'react';

const Watermark = ({
  text = 'Watermark',
  fontSize = 16,
  color = 'rgba(0, 0, 0, 0.1)',
  opacity = 0.5,
  angle = -45,
  zIndex = 9999,
  children
}) => {
  const containerRef = useRef(null);
  const canvasRef = useRef(null);

  const drawWatermark = () => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const ctx = canvas.getContext('2d');
    const { width, height } = canvas;

    ctx.clearRect(0, 0, width, height);
    ctx.font = `${fontSize}px Arial`;
    ctx.fillStyle = color;
    ctx.globalAlpha = opacity;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';

    const spacingX = width / 3;
    const spacingY = height / 3;

    for (let x = 0; x < width; x += spacingX) {
      for (let y = 0; y < height; y += spacingY) {
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate((angle * Math.PI) / 180);
        ctx.fillText(text, 0, 0);
        ctx.restore();
      }
    }
  };

  useEffect(() => {
    const resizeObserver = new ResizeObserver(() => {
      if (containerRef.current && canvasRef.current) {
        canvasRef.current.width = containerRef.current.clientWidth;
        canvasRef.current.height = containerRef.current.clientHeight;
        drawWatermark();
      }
    });

    if (containerRef.current) {
      resizeObserver.observe(containerRef.current);
    }

    return () => resizeObserver.disconnect();
  }, []);

  useEffect(() => {
    drawWatermark();
  }, [text, fontSize, color, opacity, angle]);

  return (
    <div ref={containerRef} style={{ position: 'relative', width: '100%', height: '100%' }}>
      {children}
      <canvas
        ref={canvasRef}
        style={{
          position: 'absolute',
          top: 0,
          left: 0,
          width: '100%',
          height: '100%',
          pointerEvents: 'none',
          zIndex
        }}
      />
    </div>
  );
};

export default Watermark;

使用注意事项

  • 水印组件应放在需要加水印的内容外层容器中
  • 通过zIndex控制水印的层级,确保显示在内容上方但不会干扰交互
  • 对于动态内容,需要监听容器尺寸变化并重新绘制水印
  • 考虑性能优化,避免频繁重绘

以上实现方法可以根据具体需求选择使用,Canvas方式更灵活但性能消耗较大,CSS方式更简单但定制性较弱。防止删除的保护机制可根据安全需求选择添加。

分享给朋友:

相关文章

vue如何实现登录

vue如何实现登录

实现登录功能的基本步骤 使用Vue实现登录功能通常需要结合后端API、状态管理以及路由控制。以下是常见的实现方式: 创建登录表单组件 在Vue组件中构建包含用户名和密码输入框的表单: <te…

vue自动登录如何实现

vue自动登录如何实现

实现自动登录的基本思路 自动登录通常通过结合本地存储(如localStorage或cookie)和token验证机制实现。用户首次登录成功后,服务器返回的认证token会被保存在客户端,下次打开应用时…

js如何实现继承

js如何实现继承

原型链继承 通过将子类的原型指向父类的实例实现继承。子类实例能够访问父类原型上的属性和方法。 function Parent() { this.name = 'parent'; } Paren…

h5响应式布局如何实现

h5响应式布局如何实现

使用媒体查询(Media Queries) 通过CSS媒体查询针对不同屏幕尺寸设置不同的样式规则。例如: @media screen and (max-width: 768px) { .co…

如何实现vue

如何实现vue

安装 Vue.js 使用 npm 或 yarn 安装 Vue.js。确保已安装 Node.js 环境。 npm install vue # 或 yarn add vue 创建 Vue 实例 在 HT…

vue如何实现重新实现主题

vue如何实现重新实现主题

Vue 主题切换的实现方法 使用 CSS 变量动态切换主题 定义主题相关的 CSS 变量,通过修改这些变量实现主题切换。在根元素(如 :root)中定义默认主题的变量,在特定类名下定义其他主题的变量。…