React App - Water Mark

背景

最近需要实现前端水印, 于是实现了一个通用组件, 各系统都可以直接使用, 有一定的防破解能力

开发过程

原理

直接根据页面长宽生成需要的水印div元素, 然后将元素插入到页面中即可

防破解

通过MutationObserver监听页面元素变化, 如果发现元素变化, 则重新生成水印, 由于我们的元素是由react自动生成的, 所以不需要做任何特殊改动, 只需要改变一次数据, 重新render即可

如果用户通过修改class样式实现样式覆盖, 我们这里也可以很简单的使用随机生成className字符串来解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import React, { useEffect, useState } from 'react';

const itemWidth = 100;
const itemHeight = 20;
const itemMarginLeft = 100;
const itemMarginTop = 120;
const itemTop = 20;
const itemLeft = 20;

const WaterMark: React.FC<{
/**
* 组件 classname
*/
className?: string;
/**
* 水印显示内容
*/
content: string;
}> = React.memo(({ className, content }) => {
const [itemCountState, setItemCountState] = useState({ x: 0, y: 0 });

useEffect(() => {
const onResize = _.debounce(() => {
const x = Math.floor((document.body.clientWidth - itemLeft + itemMarginLeft) / (itemWidth + itemMarginLeft));
const y = Math.floor((document.body.clientHeight - itemTop + itemMarginTop) / (itemHeight + itemMarginTop));
if (x !== itemCountState.x || y !== itemCountState.y) {
setItemCountState({ x, y });
}
}, 200);
onResize();
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);

const [refreshState, setRefreshState] = useState(false);

const containerRef = React.useRef<HTMLDivElement>(null);

useEffect(() => {
const observer = new MutationObserver(() => {
setRefreshState(true);
setTimeout(() => {
setRefreshState(false);
}, 500);
});
observer.observe(containerRef.current, { attributes: true, childList: true, subtree: true });
return () => {
observer.disconnect();
};
}, []);

return refreshState ? null : (
<div ref={containerRef}>
<div className={classNames('water-container', className)}>
{_.map(Array.from({ length: itemCountState.y }), (item, y) =>
_.map(Array.from({ length: itemCountState.x }), (item, x) => (
<div
style={{ left: `${itemLeft + (itemWidth + itemMarginLeft) * x}px`, top: `${itemTop + (itemHeight + itemMarginTop) * y}px` }}
className='water-mark'
key={`${y}-${x}`}
></div>
)),
)}
</div>
<style jsx>{`
.water-container {
.water-mark {
position: absolute;
z-index: 99999;
font-size: 14px;
color: #000;
opacity: 0.05;
filter: alpha(opacity=5);
pointer-events: none;
-webkit-transform: rotate(-15deg);
-moz-transform: rotate(-15deg);
-o-transform: rotate(-15deg);
-ms-transform: rotate(-15deg);
transform: rotate(-15deg);
max-width: 400px;
word-wrap: break-word;
&::before {
content: '${content}';
}
}
}
`}</style>
</div>
);
});

export default WaterMark;
作者

Mosby

发布于

2020-11-02

许可协议

评论