背景
最近需要实现前端水印, 于是实现了一个通用组件, 各系统都可以直接使用, 有一定的防破解能力
开发过程
原理
直接根据页面长宽生成需要的水印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?: 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;
|