mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-05 01:19:45 +08:00
8a3870fc31
* 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
157 lines
5.0 KiB
TypeScript
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;
|
|
}
|