如何实现一个react水印组件
实现React水印组件的方法
使用Canvas绘制水印
通过Canvas动态生成水印图案,可以灵活控制水印的样式和位置。

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重复背景图实现简单水印效果,适合静态水印需求。

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方式更简单但定制性较弱。防止删除的保护机制可根据安全需求选择添加。






