ant-design/components/watermark/useContent.tsx
二货爱吃白萝卜 8a3870fc31
feat: Watermark support nest Modal & Drawer (#44104)
* docs: add demo

* refactor: init

* refactor: of it

* refactor: simple content

* chore: unique func

* chore: refactor

* chore: support modal watermark

* feat: support nest watermark

* test: add test case

* chore: fix lint

* chore: bump rc-image

* test: add test case

* refactor: use same func
2023-08-08 16:48:26 +08:00

157 lines
5.0 KiB
TypeScript

import type { WatermarkProps } from '.';
import useToken from '../theme/useToken';
import { BaseSize, FontGap } from './useWatermark';
import { getPixelRatio, rotateWatermark } from './utils';
export default function useContent(
props: Pick<WatermarkProps, 'width' | 'height' | 'image' | 'content' | 'font'> &
Required<Pick<WatermarkProps, 'rotate' | 'gap'>>,
callback: (base64Url: string, markWidth: number) => void,
) {
const { rotate, width, height, image, content, font = {}, gap } = props;
const [, token] = useToken();
const {
color = token.colorFill,
fontSize = token.fontSizeLG,
fontWeight = 'normal',
fontStyle = 'normal',
fontFamily = 'sans-serif',
} = font;
const [gapX, gapY] = gap;
/**
* Get the width and height of the watermark. The default values are as follows
* Image: [120, 64]; Content: It's calculated by content;
*/
const getMarkSize = (ctx: CanvasRenderingContext2D) => {
let defaultWidth = 120;
let defaultHeight = 64;
if (!image && ctx.measureText) {
ctx.font = `${Number(fontSize)}px ${fontFamily}`;
const contents = Array.isArray(content) ? content : [content];
const widths = contents.map((item) => ctx.measureText(item!).width);
defaultWidth = Math.ceil(Math.max(...widths));
defaultHeight = Number(fontSize) * contents.length + (contents.length - 1) * FontGap;
}
return [width ?? defaultWidth, height ?? defaultHeight] as const;
};
const fillTexts = (
ctx: CanvasRenderingContext2D,
drawX: number,
drawY: number,
drawWidth: number,
drawHeight: number,
) => {
const ratio = getPixelRatio();
const mergedFontSize = Number(fontSize) * ratio;
ctx.font = `${fontStyle} normal ${fontWeight} ${mergedFontSize}px/${drawHeight}px ${fontFamily}`;
ctx.fillStyle = color;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.translate(drawWidth / 2, 0);
const contents = Array.isArray(content) ? content : [content];
contents?.forEach((item, index) => {
ctx.fillText(item ?? '', drawX, drawY + index * (mergedFontSize + FontGap * ratio));
});
};
const drawText = (
canvas: HTMLCanvasElement,
ctx: CanvasRenderingContext2D,
drawX: number,
drawY: number,
drawWidth: number,
drawHeight: number,
alternateRotateX: number,
alternateRotateY: number,
alternateDrawX: number,
alternateDrawY: number,
markWidth: number,
) => {
fillTexts(ctx, drawX, drawY, drawWidth, drawHeight);
/** Fill the interleaved text after rotation */
ctx.restore();
rotateWatermark(ctx, alternateRotateX, alternateRotateY, rotate);
fillTexts(ctx, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
callback(canvas.toDataURL(), markWidth);
};
const renderWatermark = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (ctx) {
const ratio = getPixelRatio();
const [markWidth, markHeight] = getMarkSize(ctx);
const canvasWidth = (gapX + markWidth) * ratio;
const canvasHeight = (gapY + markHeight) * ratio;
canvas.setAttribute('width', `${canvasWidth * BaseSize}px`);
canvas.setAttribute('height', `${canvasHeight * BaseSize}px`);
const drawX = (gapX * ratio) / 2;
const drawY = (gapY * ratio) / 2;
const drawWidth = markWidth * ratio;
const drawHeight = markHeight * ratio;
const rotateX = (drawWidth + gapX * ratio) / 2;
const rotateY = (drawHeight + gapY * ratio) / 2;
/** Alternate drawing parameters */
const alternateDrawX = drawX + canvasWidth;
const alternateDrawY = drawY + canvasHeight;
const alternateRotateX = rotateX + canvasWidth;
const alternateRotateY = rotateY + canvasHeight;
ctx.save();
rotateWatermark(ctx, rotateX, rotateY, rotate);
if (image) {
const img = new Image();
img.onload = () => {
ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
/** Draw interleaved pictures after rotation */
ctx.restore();
rotateWatermark(ctx, alternateRotateX, alternateRotateY, rotate);
ctx.drawImage(img, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
callback(canvas.toDataURL(), markWidth);
};
img.onerror = () =>
drawText(
canvas,
ctx,
drawX,
drawY,
drawWidth,
drawHeight,
alternateRotateX,
alternateRotateY,
alternateDrawX,
alternateDrawY,
markWidth,
);
img.crossOrigin = 'anonymous';
img.referrerPolicy = 'no-referrer';
img.src = image;
} else {
drawText(
canvas,
ctx,
drawX,
drawY,
drawWidth,
drawHeight,
alternateRotateX,
alternateRotateY,
alternateDrawX,
alternateDrawY,
markWidth,
);
}
}
};
return renderWatermark;
}