Skip to content

Commit

Permalink
Feature/tiennecheng watermark markchange (#2459)
Browse files Browse the repository at this point in the history
* feat(watermark): 水印组件-修改水印节点不影响水印展示

水印组件-修改水印节点不影响水印展示

* feat(watermark): 水印数据渲染问题优化

水印数据渲染问题优化
  • Loading branch information
tingtingcheng6 authored Aug 24, 2023
1 parent f3e4ddd commit 5a5466b
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 29 deletions.
88 changes: 66 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 @@ -8,6 +8,7 @@ import useConfig from '../hooks/useConfig';
import useMutationObserver from '../_util/useMutationObserver';
import { TdWatermarkProps } from './type';
import { watermarkDefaultProps as defaultProps } from './defaultProps';
import { getStyleStr } from './utils';

export interface WatermarkProps extends TdWatermarkProps, StyledProps {}

Expand Down Expand Up @@ -43,11 +44,15 @@ const Watermark: React.FC<WatermarkProps> = ({
}
const clsName = `${classPrefix}-watermark`;
const [base64Url, setBase64Url] = useState('');
const styleStr = useRef('');
const maskclassName = useRef(className);
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,28 +73,87 @@ const Watermark: React.FC<WatermarkProps> = ({
);
}, [width, height, rotate, zIndex, lineSpace, alpha, offsetLeft, offsetTop, gapX, gapY, watermarkContent]);

// 水印节点 - styleStr
useEffect(() => {
styleStr.current = 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,
});
}, [zIndex, gapX, width, movable, isRepeat, base64Url, moveInterval, style]);

// 水印节点 - className
useEffect(() => {
maskclassName.current = className;
}, [className]);

// 水印节点 - 渲染
const renderWatermark = useCallback(() => {
// 停止监听
stopObservation.current = true;
// 删除之前
watermarkImgRef.current?.remove?.();
watermarkImgRef.current = undefined;
// 创建新的
watermarkImgRef.current = document.createElement('div');
watermarkImgRef.current.setAttribute('style', styleStr.current);
if (maskclassName.current) {
watermarkImgRef.current.setAttribute('class', maskclassName.current);
}
watermarkRef.current?.append(watermarkImgRef.current);
// 继续监听
setTimeout(() => {
stopObservation.current = false;
});
}, []);

// 水印节点 - 初始化渲染
useEffect(() => {
renderWatermark();
}, [renderWatermark, zIndex, gapX, width, movable, isRepeat, base64Url, moveInterval, style, className]);

// 水印节点 - 变化时重新渲染
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();
}
});
});

// 组件父节点 - 增加keyframes
const parent = useRef<HTMLElement>();
useEffect(() => {
parent.current = watermarkRef.current.parentElement;
const keyframesStyle = randomMovingStyle();
injectStyle(keyframesStyle);
}, []);

// 水印节点的父节点 - 防删除
useMutationObserver(typeof document !== 'undefined' ? document.body : null, (mutations) => {
if (removable) return;
mutations.forEach((mutation) => {
Expand All @@ -108,26 +172,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();
});
});
7 changes: 7 additions & 0 deletions src/watermark/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const toLowercaseSeparator = (key: string) => key.replace(/([A-Z])/g, '-$1').toLowerCase();

// style对象转字符串
export const getStyleStr = (style: React.CSSProperties): string =>
Object.keys(style)
.map((key: keyof React.CSSProperties) => `${toLowercaseSeparator(key)}: ${style[key]};`)
.join(' ');

0 comments on commit 5a5466b

Please sign in to comment.