mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-02 03:59:01 +08:00
chore: Watermark uses MutateObserver (#40081)
* chore: Watermark uses MutateObserver * chore: upgrade @rc-component/mutate-observer
This commit is contained in:
parent
52478c78d3
commit
26c3c326ad
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import Watermark from '..';
|
import Watermark from '..';
|
||||||
import mountTest from '../../../tests/shared/mountTest';
|
import mountTest from '../../../tests/shared/mountTest';
|
||||||
import rtlTest from '../../../tests/shared/rtlTest';
|
import rtlTest from '../../../tests/shared/rtlTest';
|
||||||
import { render, waitFor } from '../../../tests/utils';
|
import { render, waitFor, waitFakeTimer } from '../../../tests/utils';
|
||||||
|
|
||||||
describe('Watermark', () => {
|
describe('Watermark', () => {
|
||||||
mountTest(Watermark);
|
mountTest(Watermark);
|
||||||
@ -67,6 +67,7 @@ describe('Watermark', () => {
|
|||||||
it('MutationObserver should work properly', async () => {
|
it('MutationObserver should work properly', async () => {
|
||||||
const { container } = render(<Watermark className="watermark" content="MutationObserver" />);
|
const { container } = render(<Watermark className="watermark" content="MutationObserver" />);
|
||||||
const target = container.querySelector<HTMLDivElement>('.watermark div');
|
const target = container.querySelector<HTMLDivElement>('.watermark div');
|
||||||
|
await waitFakeTimer();
|
||||||
target?.remove();
|
target?.remove();
|
||||||
await waitFor(() => expect(target).toBeTruthy());
|
await waitFor(() => expect(target).toBeTruthy());
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
@ -77,6 +78,7 @@ describe('Watermark', () => {
|
|||||||
<Watermark offset={[-200, -200]} className="watermark" content="MutationObserver" />,
|
<Watermark offset={[-200, -200]} className="watermark" content="MutationObserver" />,
|
||||||
);
|
);
|
||||||
const target = container.querySelector<HTMLDivElement>('.watermark div');
|
const target = container.querySelector<HTMLDivElement>('.watermark div');
|
||||||
|
await waitFakeTimer();
|
||||||
target?.setAttribute('style', '');
|
target?.setAttribute('style', '');
|
||||||
await waitFor(() => expect(target).toBeTruthy());
|
await waitFor(() => expect(target).toBeTruthy());
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import useMutationObserver from './useMutationObserver';
|
import MutateObserver from '@rc-component/mutate-observer';
|
||||||
import { getStyleStr, getPixelRatio, rotateWatermark } from './utils';
|
import { getStyleStr, getPixelRatio, rotateWatermark, reRendering } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base size of the canvas, 1 for parallel layout and 2 for alternate layout
|
* Base size of the canvas, 1 for parallel layout and 2 for alternate layout
|
||||||
@ -96,7 +96,7 @@ const Watermark: React.FC<WatermarkProps> = (props) => {
|
|||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const watermarkRef = useRef<HTMLDivElement>();
|
const watermarkRef = useRef<HTMLDivElement>();
|
||||||
const { createObserver, destroyObserver, reRendering } = useMutationObserver();
|
const stopObservation = useRef(false);
|
||||||
|
|
||||||
const destroyWatermark = () => {
|
const destroyWatermark = () => {
|
||||||
if (watermarkRef.current) {
|
if (watermarkRef.current) {
|
||||||
@ -107,7 +107,7 @@ const Watermark: React.FC<WatermarkProps> = (props) => {
|
|||||||
|
|
||||||
const appendWatermark = (base64Url: string, markWidth: number) => {
|
const appendWatermark = (base64Url: string, markWidth: number) => {
|
||||||
if (containerRef.current && watermarkRef.current) {
|
if (containerRef.current && watermarkRef.current) {
|
||||||
destroyObserver();
|
stopObservation.current = true;
|
||||||
watermarkRef.current.setAttribute(
|
watermarkRef.current.setAttribute(
|
||||||
'style',
|
'style',
|
||||||
getStyleStr({
|
getStyleStr({
|
||||||
@ -117,14 +117,9 @@ const Watermark: React.FC<WatermarkProps> = (props) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
containerRef.current?.append(watermarkRef.current);
|
containerRef.current?.append(watermarkRef.current);
|
||||||
createObserver(containerRef.current, (mutations) => {
|
// Delayed execution
|
||||||
mutations.forEach((mutation) => {
|
setTimeout(() => {
|
||||||
if (reRendering(mutation, watermarkRef.current)) {
|
stopObservation.current = false;
|
||||||
destroyWatermark();
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
||||||
renderWatermark();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -221,6 +216,18 @@ const Watermark: React.FC<WatermarkProps> = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onMutate = (mutations: MutationRecord[]) => {
|
||||||
|
if (stopObservation.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
if (reRendering(mutation, watermarkRef.current)) {
|
||||||
|
destroyWatermark();
|
||||||
|
renderWatermark();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(renderWatermark, [
|
useEffect(renderWatermark, [
|
||||||
rotate,
|
rotate,
|
||||||
zIndex,
|
zIndex,
|
||||||
@ -240,9 +247,11 @@ const Watermark: React.FC<WatermarkProps> = (props) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<MutateObserver onMutate={onMutate}>
|
||||||
<div ref={containerRef} className={className} style={{ position: 'relative', ...style }}>
|
<div ref={containerRef} className={className} style={{ position: 'relative', ...style }}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
</MutateObserver>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
|
||||||
|
|
||||||
export default function useMutationObserver() {
|
|
||||||
const instance = useRef<MutationObserver>();
|
|
||||||
|
|
||||||
const destroyObserver = () => {
|
|
||||||
if (instance.current) {
|
|
||||||
instance.current.takeRecords();
|
|
||||||
instance.current.disconnect();
|
|
||||||
instance.current = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const createObserver = (target: Node, callback: MutationCallback) => {
|
|
||||||
if (MutationObserver) {
|
|
||||||
destroyObserver();
|
|
||||||
instance.current = new MutationObserver(callback);
|
|
||||||
instance.current.observe(target, {
|
|
||||||
childList: true,
|
|
||||||
subtree: true,
|
|
||||||
attributeFilter: ['style', 'class'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => destroyObserver, []);
|
|
||||||
|
|
||||||
const reRendering = (mutation: MutationRecord, watermarkElement?: HTMLElement) => {
|
|
||||||
let flag = false;
|
|
||||||
// Whether to delete the watermark node
|
|
||||||
if (mutation.removedNodes.length) {
|
|
||||||
flag = Array.from(mutation.removedNodes).some((node) => node === watermarkElement);
|
|
||||||
}
|
|
||||||
// Whether the watermark dom property value has been modified
|
|
||||||
if (mutation.type === 'attributes' && mutation.target === watermarkElement) {
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
return flag;
|
|
||||||
};
|
|
||||||
|
|
||||||
return { createObserver, destroyObserver, reRendering };
|
|
||||||
}
|
|
@ -25,3 +25,17 @@ export function rotateWatermark(
|
|||||||
ctx.rotate((Math.PI / 180) * Number(rotate));
|
ctx.rotate((Math.PI / 180) * Number(rotate));
|
||||||
ctx.translate(-rotateX, -rotateY);
|
ctx.translate(-rotateX, -rotateY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Whether to re-render the watermark */
|
||||||
|
export const reRendering = (mutation: MutationRecord, watermarkElement?: HTMLElement) => {
|
||||||
|
let flag = false;
|
||||||
|
// Whether to delete the watermark node
|
||||||
|
if (mutation.removedNodes.length) {
|
||||||
|
flag = Array.from(mutation.removedNodes).some((node) => node === watermarkElement);
|
||||||
|
}
|
||||||
|
// Whether the watermark dom property value has been modified
|
||||||
|
if (mutation.type === 'attributes' && mutation.target === watermarkElement) {
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
};
|
||||||
|
@ -113,6 +113,7 @@
|
|||||||
"@ant-design/react-slick": "~1.0.0",
|
"@ant-design/react-slick": "~1.0.0",
|
||||||
"@babel/runtime": "^7.18.3",
|
"@babel/runtime": "^7.18.3",
|
||||||
"@ctrl/tinycolor": "^3.4.0",
|
"@ctrl/tinycolor": "^3.4.0",
|
||||||
|
"@rc-component/mutate-observer": "^1.0.0",
|
||||||
"@rc-component/tour": "~1.1.0",
|
"@rc-component/tour": "~1.1.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"copy-to-clipboard": "^3.2.0",
|
"copy-to-clipboard": "^3.2.0",
|
||||||
|
Loading…
Reference in New Issue
Block a user