Skip to content

Commit

Permalink
feat(watermark): 水印组件-修改水印节点不影响水印展示
Browse files Browse the repository at this point in the history
水印组件-修改水印节点不影响水印展示
  • Loading branch information
tingtingcheng6 committed Aug 23, 2023
1 parent 99d2251 commit 0b6edc5
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 29 deletions.
81 changes: 59 additions & 22 deletions src/watermark/Watermark.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-nested-ternary */
import React, { useState, useEffect, useRef } from 'react';
import React, { useCallback, useState, useEffect, useRef } from 'react';
import { StyledProps } from '../common';
import generateBase64Url from '../_common/js/watermark/generateBase64Url';
import randomMovingStyle from '../_common/js/watermark/randomMovingStyle';
Expand All @@ -11,6 +11,13 @@ import { watermarkDefaultProps as defaultProps } from './defaultProps';

export interface WatermarkProps extends TdWatermarkProps, StyledProps {}

const toLowercaseSeparator = (key: string) => key.replace(/([A-Z])/g, '-$1').toLowerCase();

const getStyleStr = (style: React.CSSProperties): string =>
Object.keys(style)
.map((key: keyof React.CSSProperties) => `${toLowercaseSeparator(key)}: ${style[key]};`)
.join(' ');

const Watermark: React.FC<WatermarkProps> = ({
alpha = defaultProps.alpha,
x = 200,
Expand Down Expand Up @@ -45,9 +52,11 @@ const Watermark: React.FC<WatermarkProps> = ({
const [base64Url, setBase64Url] = useState('');
const watermarkRef = useRef<HTMLDivElement>();
const watermarkImgRef = useRef<HTMLDivElement>();
const stopObservation = useRef(false);
const offsetLeft = offset[0] || gapX / 2;
const offsetTop = offset[1] || gapY / 2;

// 水印节点的背景base64
useEffect(() => {
generateBase64Url(
{
Expand All @@ -68,18 +77,66 @@ const Watermark: React.FC<WatermarkProps> = ({
);
}, [width, height, rotate, zIndex, lineSpace, alpha, offsetLeft, offsetTop, gapX, gapY, watermarkContent]);

// 插入水印节点
const renderWatermark = useCallback(() => {
// 停止监听
stopObservation.current = true;
// 删除之前
watermarkImgRef.current?.remove?.();
watermarkImgRef.current = undefined;
// 创建新的
watermarkImgRef.current = document.createElement('div');
watermarkImgRef.current.setAttribute(
'style',
getStyleStr({
zIndex,
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
width: '100%',
height: '100%',
backgroundSize: `${gapX + width}px`,
pointerEvents: 'none',
backgroundRepeat: movable ? 'no-repeat' : isRepeat ? 'repeat' : 'no-repeat',
backgroundImage: `url('${base64Url}')`,
animation: movable ? `watermark infinite ${(moveInterval * 4) / 60}s` : 'none',
...style,
}),
);
if (className) {
watermarkImgRef.current.setAttribute('class', className);
}
watermarkRef.current?.append(watermarkImgRef.current);
// 继续监听
setTimeout(() => {
stopObservation.current = false;
});
}, [zIndex, gapX, width, movable, isRepeat, base64Url, moveInterval, style, className]);
useEffect(() => {
renderWatermark();
}, [renderWatermark, base64Url]);

// 监听水印节点
useMutationObserver(watermarkRef.current, (mutations) => {
if (stopObservation.current) return;
if (removable) return;
mutations.forEach((mutation) => {
// 水印节点被删除
if (mutation.type === 'childList') {
const removeNodes = mutation.removedNodes;
removeNodes.forEach((node) => {
const element = node as HTMLElement;
if (element === watermarkImgRef.current) {
watermarkRef.current.appendChild(element);
renderWatermark();
}
});
}
// 水印节点其他变化
if (mutation.target === watermarkImgRef.current) {
renderWatermark();
}
});
});

Expand Down Expand Up @@ -108,26 +165,6 @@ const Watermark: React.FC<WatermarkProps> = ({
return (
<div style={{ position: 'relative', overflow: 'hidden' }} className={clsName} ref={watermarkRef}>
{children || content}
<div
ref={watermarkImgRef}
className={className}
style={{
zIndex,
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
width: '100%',
height: '100%',
backgroundSize: `${gapX + width}px`,
pointerEvents: 'none',
backgroundRepeat: movable ? 'no-repeat' : isRepeat ? 'repeat' : 'no-repeat',
backgroundImage: `url('${base64Url}')`,
animation: movable ? `watermark infinite ${(moveInterval * 4) / 60}s` : 'none',
...style,
}}
/>
</div>
);
};
Expand Down
20 changes: 13 additions & 7 deletions src/watermark/__tests__/watermark.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,26 @@ describe('Watermark 组件测试', () => {
const watermarkWrapParent = watermarkWrap.parentElement;
const watermarkEle = wrapper.container.querySelector('.test-observer');

// 删除了水印wrap元素,还会被立即追加回去
watermarkWrapParent.removeChild(watermarkWrap);
const afterWrapRemove = wrapper.container.querySelector('.t-watermark');
expect(afterWrapRemove).toBeNull();
await mockDelay(10);
const waitAfterWrapRemove = wrapper.container.querySelector('.t-watermark');
expect(waitAfterWrapRemove).not.toBeNull();

// 删除了水印元素,还会被立即追加回去
watermarkWrap.removeChild(watermarkEle);
const afterMarkRemove = wrapper.container.querySelector('.test-observer');
expect(afterMarkRemove).toBeNull();
// 删除了水印元素,还会被立即追加回去
await mockDelay(10);
const waitAfterMarkRemove = wrapper.container.querySelector('.test-observer');
expect(waitAfterMarkRemove).not.toBeNull();

watermarkWrapParent.removeChild(watermarkWrap);
const afterWrapRemove = wrapper.container.querySelector('.t-watermark');
expect(afterWrapRemove).toBeNull();
// 删除了水印wrap元素,还会被立即追加回去
// 修改水印元素的属性,会立即还原
watermarkEle.setAttribute('any', '11');
await mockDelay(10);
const waitAfterWrapRemove = wrapper.container.querySelector('.t-watermark');
expect(waitAfterWrapRemove).not.toBeNull();
const waitAfterAttrChange = wrapper.container.querySelector('.test-observer');
expect(waitAfterAttrChange.getAttribute('any')).toBeNull();
});
});

0 comments on commit 0b6edc5

Please sign in to comment.