UNPKG

antd

Version:

An enterprise-class UI design language and React components implementation

206 lines (205 loc) 7.98 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _react = _interopRequireWildcard(require("react")); var _mutateObserver = _interopRequireDefault(require("@rc-component/mutate-observer")); var _utils = require("./utils"); /** * Base size of the canvas, 1 for parallel layout and 2 for alternate layout * Only alternate layout is currently supported */ const BaseSize = 2; const FontGap = 3; const Watermark = props => { var _a, _b; const { /** * The antd content layer zIndex is basically below 10 * https://github.com/ant-design/ant-design/blob/6192403b2ce517c017f9e58a32d58774921c10cd/components/style/themes/default.less#L335 */ zIndex = 9, rotate = -22, width, height, image, content, font = {}, style, className, gap = [100, 100], offset, children } = props; const { color = 'rgba(0,0,0,.15)', fontSize = 16, fontWeight = 'normal', fontStyle = 'normal', fontFamily = 'sans-serif' } = font; const [gapX, gapY] = gap; const gapXCenter = gapX / 2; const gapYCenter = gapY / 2; const offsetLeft = (_a = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _a !== void 0 ? _a : gapXCenter; const offsetTop = (_b = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _b !== void 0 ? _b : gapYCenter; const getMarkStyle = () => { const markStyle = { zIndex, position: 'absolute', left: 0, top: 0, width: '100%', height: '100%', pointerEvents: 'none', backgroundRepeat: 'repeat' }; /** Calculate the style of the offset */ let positionLeft = offsetLeft - gapXCenter; let positionTop = offsetTop - gapYCenter; if (positionLeft > 0) { markStyle.left = `${positionLeft}px`; markStyle.width = `calc(100% - ${positionLeft}px)`; positionLeft = 0; } if (positionTop > 0) { markStyle.top = `${positionTop}px`; markStyle.height = `calc(100% - ${positionTop}px)`; positionTop = 0; } markStyle.backgroundPosition = `${positionLeft}px ${positionTop}px`; return markStyle; }; const containerRef = (0, _react.useRef)(null); const watermarkRef = (0, _react.useRef)(); const stopObservation = (0, _react.useRef)(false); const destroyWatermark = () => { if (watermarkRef.current) { watermarkRef.current.remove(); watermarkRef.current = undefined; } }; const appendWatermark = (base64Url, markWidth) => { var _a; if (containerRef.current && watermarkRef.current) { stopObservation.current = true; watermarkRef.current.setAttribute('style', (0, _utils.getStyleStr)(Object.assign(Object.assign({}, getMarkStyle()), { backgroundImage: `url('${base64Url}')`, backgroundSize: `${(gapX + markWidth) * BaseSize}px` }))); (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.append(watermarkRef.current); // Delayed execution setTimeout(() => { stopObservation.current = false; }); } }; /** * 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 => { 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.apply(Math, (0, _toConsumableArray2.default)(widths))); defaultHeight = Number(fontSize) * contents.length + (contents.length - 1) * FontGap; } return [width !== null && width !== void 0 ? width : defaultWidth, height !== null && height !== void 0 ? height : defaultHeight]; }; const fillTexts = (ctx, drawX, drawY, drawWidth, drawHeight) => { const ratio = (0, _utils.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 === null || contents === void 0 ? void 0 : contents.forEach((item, index) => { ctx.fillText(item !== null && item !== void 0 ? item : '', drawX, drawY + index * (mergedFontSize + FontGap * ratio)); }); }; const renderWatermark = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); if (ctx) { if (!watermarkRef.current) { watermarkRef.current = document.createElement('div'); } const ratio = (0, _utils.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(); (0, _utils.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(); (0, _utils.rotateWatermark)(ctx, alternateRotateX, alternateRotateY, rotate); ctx.drawImage(img, alternateDrawX, alternateDrawY, drawWidth, drawHeight); appendWatermark(canvas.toDataURL(), markWidth); }; img.crossOrigin = 'anonymous'; img.referrerPolicy = 'no-referrer'; img.src = image; } else { fillTexts(ctx, drawX, drawY, drawWidth, drawHeight); /** Fill the interleaved text after rotation */ ctx.restore(); (0, _utils.rotateWatermark)(ctx, alternateRotateX, alternateRotateY, rotate); fillTexts(ctx, alternateDrawX, alternateDrawY, drawWidth, drawHeight); appendWatermark(canvas.toDataURL(), markWidth); } } }; const onMutate = mutations => { if (stopObservation.current) { return; } mutations.forEach(mutation => { if ((0, _utils.reRendering)(mutation, watermarkRef.current)) { destroyWatermark(); renderWatermark(); } }); }; (0, _react.useEffect)(renderWatermark, [rotate, zIndex, width, height, image, content, color, fontSize, fontWeight, fontStyle, fontFamily, gapX, gapY, offsetLeft, offsetTop]); return /*#__PURE__*/_react.default.createElement(_mutateObserver.default, { onMutate: onMutate }, /*#__PURE__*/_react.default.createElement("div", { ref: containerRef, className: className, style: Object.assign({ position: 'relative' }, style) }, children)); }; if (process.env.NODE_ENV !== 'production') { Watermark.displayName = 'Watermark'; } var _default = Watermark; exports.default = _default;