index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.watermarkProps = exports.default = void 0;
  7. var _vue = require("vue");
  8. var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
  9. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  10. var _utils = require("./utils");
  11. var _type = require("../_util/type");
  12. var _useMutationObserver = require("../_util/hooks/_vueuse/useMutationObserver");
  13. var _propsUtil = require("../_util/props-util");
  14. var _internal = require("../theme/internal");
  15. /**
  16. * Base size of the canvas, 1 for parallel layout and 2 for alternate layout
  17. * Only alternate layout is currently supported
  18. */
  19. const BaseSize = 2;
  20. const FontGap = 3;
  21. const watermarkProps = () => ({
  22. zIndex: Number,
  23. rotate: Number,
  24. width: Number,
  25. height: Number,
  26. image: String,
  27. content: (0, _type.someType)([String, Array]),
  28. font: (0, _type.objectType)(),
  29. rootClassName: String,
  30. gap: (0, _type.arrayType)(),
  31. offset: (0, _type.arrayType)()
  32. });
  33. exports.watermarkProps = watermarkProps;
  34. const Watermark = (0, _vue.defineComponent)({
  35. name: 'AWatermark',
  36. inheritAttrs: false,
  37. props: (0, _propsUtil.initDefaultProps)(watermarkProps(), {
  38. zIndex: 9,
  39. rotate: -22,
  40. font: {},
  41. gap: [100, 100]
  42. }),
  43. setup(props, _ref) {
  44. let {
  45. slots,
  46. attrs
  47. } = _ref;
  48. const [, token] = (0, _internal.useToken)();
  49. const containerRef = (0, _vue.shallowRef)();
  50. const watermarkRef = (0, _vue.shallowRef)();
  51. const stopObservation = (0, _vue.shallowRef)(false);
  52. const gapX = (0, _vue.computed)(() => {
  53. var _a, _b;
  54. return (_b = (_a = props.gap) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 100;
  55. });
  56. const gapY = (0, _vue.computed)(() => {
  57. var _a, _b;
  58. return (_b = (_a = props.gap) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : 100;
  59. });
  60. const gapXCenter = (0, _vue.computed)(() => gapX.value / 2);
  61. const gapYCenter = (0, _vue.computed)(() => gapY.value / 2);
  62. const offsetLeft = (0, _vue.computed)(() => {
  63. var _a, _b;
  64. return (_b = (_a = props.offset) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : gapXCenter.value;
  65. });
  66. const offsetTop = (0, _vue.computed)(() => {
  67. var _a, _b;
  68. return (_b = (_a = props.offset) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : gapYCenter.value;
  69. });
  70. const fontSize = (0, _vue.computed)(() => {
  71. var _a, _b;
  72. return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.fontSize) !== null && _b !== void 0 ? _b : token.value.fontSizeLG;
  73. });
  74. const fontWeight = (0, _vue.computed)(() => {
  75. var _a, _b;
  76. return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.fontWeight) !== null && _b !== void 0 ? _b : 'normal';
  77. });
  78. const fontStyle = (0, _vue.computed)(() => {
  79. var _a, _b;
  80. return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.fontStyle) !== null && _b !== void 0 ? _b : 'normal';
  81. });
  82. const fontFamily = (0, _vue.computed)(() => {
  83. var _a, _b;
  84. return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.fontFamily) !== null && _b !== void 0 ? _b : 'sans-serif';
  85. });
  86. const color = (0, _vue.computed)(() => {
  87. var _a, _b;
  88. return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.color) !== null && _b !== void 0 ? _b : token.value.colorFill;
  89. });
  90. const markStyle = (0, _vue.computed)(() => {
  91. var _a;
  92. const markStyle = {
  93. zIndex: (_a = props.zIndex) !== null && _a !== void 0 ? _a : 9,
  94. position: 'absolute',
  95. left: 0,
  96. top: 0,
  97. width: '100%',
  98. height: '100%',
  99. pointerEvents: 'none',
  100. backgroundRepeat: 'repeat'
  101. };
  102. /** Calculate the style of the offset */
  103. let positionLeft = offsetLeft.value - gapXCenter.value;
  104. let positionTop = offsetTop.value - gapYCenter.value;
  105. if (positionLeft > 0) {
  106. markStyle.left = `${positionLeft}px`;
  107. markStyle.width = `calc(100% - ${positionLeft}px)`;
  108. positionLeft = 0;
  109. }
  110. if (positionTop > 0) {
  111. markStyle.top = `${positionTop}px`;
  112. markStyle.height = `calc(100% - ${positionTop}px)`;
  113. positionTop = 0;
  114. }
  115. markStyle.backgroundPosition = `${positionLeft}px ${positionTop}px`;
  116. return markStyle;
  117. });
  118. const destroyWatermark = () => {
  119. if (watermarkRef.value) {
  120. watermarkRef.value.remove();
  121. watermarkRef.value = undefined;
  122. }
  123. };
  124. const appendWatermark = (base64Url, markWidth) => {
  125. var _a;
  126. if (containerRef.value && watermarkRef.value) {
  127. stopObservation.value = true;
  128. watermarkRef.value.setAttribute('style', (0, _utils.getStyleStr)((0, _extends2.default)((0, _extends2.default)({}, markStyle.value), {
  129. backgroundImage: `url('${base64Url}')`,
  130. backgroundSize: `${(gapX.value + markWidth) * BaseSize}px`
  131. })));
  132. (_a = containerRef.value) === null || _a === void 0 ? void 0 : _a.append(watermarkRef.value);
  133. // Delayed execution
  134. setTimeout(() => {
  135. stopObservation.value = false;
  136. });
  137. }
  138. };
  139. /**
  140. * Get the width and height of the watermark. The default values are as follows
  141. * Image: [120, 64]; Content: It's calculated by content;
  142. */
  143. const getMarkSize = ctx => {
  144. let defaultWidth = 120;
  145. let defaultHeight = 64;
  146. const content = props.content;
  147. const image = props.image;
  148. const width = props.width;
  149. const height = props.height;
  150. if (!image && ctx.measureText) {
  151. ctx.font = `${Number(fontSize.value)}px ${fontFamily.value}`;
  152. const contents = Array.isArray(content) ? content : [content];
  153. const widths = contents.map(item => ctx.measureText(item).width);
  154. defaultWidth = Math.ceil(Math.max(...widths));
  155. defaultHeight = Number(fontSize.value) * contents.length + (contents.length - 1) * FontGap;
  156. }
  157. return [width !== null && width !== void 0 ? width : defaultWidth, height !== null && height !== void 0 ? height : defaultHeight];
  158. };
  159. const fillTexts = (ctx, drawX, drawY, drawWidth, drawHeight) => {
  160. const ratio = (0, _utils.getPixelRatio)();
  161. const content = props.content;
  162. const mergedFontSize = Number(fontSize.value) * ratio;
  163. ctx.font = `${fontStyle.value} normal ${fontWeight.value} ${mergedFontSize}px/${drawHeight}px ${fontFamily.value}`;
  164. ctx.fillStyle = color.value;
  165. ctx.textAlign = 'center';
  166. ctx.textBaseline = 'top';
  167. ctx.translate(drawWidth / 2, 0);
  168. const contents = Array.isArray(content) ? content : [content];
  169. contents === null || contents === void 0 ? void 0 : contents.forEach((item, index) => {
  170. ctx.fillText(item !== null && item !== void 0 ? item : '', drawX, drawY + index * (mergedFontSize + FontGap * ratio));
  171. });
  172. };
  173. const renderWatermark = () => {
  174. var _a;
  175. const canvas = document.createElement('canvas');
  176. const ctx = canvas.getContext('2d');
  177. const image = props.image;
  178. const rotate = (_a = props.rotate) !== null && _a !== void 0 ? _a : -22;
  179. if (ctx) {
  180. if (!watermarkRef.value) {
  181. watermarkRef.value = document.createElement('div');
  182. }
  183. const ratio = (0, _utils.getPixelRatio)();
  184. const [markWidth, markHeight] = getMarkSize(ctx);
  185. const canvasWidth = (gapX.value + markWidth) * ratio;
  186. const canvasHeight = (gapY.value + markHeight) * ratio;
  187. canvas.setAttribute('width', `${canvasWidth * BaseSize}px`);
  188. canvas.setAttribute('height', `${canvasHeight * BaseSize}px`);
  189. const drawX = gapX.value * ratio / 2;
  190. const drawY = gapY.value * ratio / 2;
  191. const drawWidth = markWidth * ratio;
  192. const drawHeight = markHeight * ratio;
  193. const rotateX = (drawWidth + gapX.value * ratio) / 2;
  194. const rotateY = (drawHeight + gapY.value * ratio) / 2;
  195. /** Alternate drawing parameters */
  196. const alternateDrawX = drawX + canvasWidth;
  197. const alternateDrawY = drawY + canvasHeight;
  198. const alternateRotateX = rotateX + canvasWidth;
  199. const alternateRotateY = rotateY + canvasHeight;
  200. ctx.save();
  201. (0, _utils.rotateWatermark)(ctx, rotateX, rotateY, rotate);
  202. if (image) {
  203. const img = new Image();
  204. img.onload = () => {
  205. ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
  206. /** Draw interleaved pictures after rotation */
  207. ctx.restore();
  208. (0, _utils.rotateWatermark)(ctx, alternateRotateX, alternateRotateY, rotate);
  209. ctx.drawImage(img, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
  210. appendWatermark(canvas.toDataURL(), markWidth);
  211. };
  212. img.crossOrigin = 'anonymous';
  213. img.referrerPolicy = 'no-referrer';
  214. img.src = image;
  215. } else {
  216. fillTexts(ctx, drawX, drawY, drawWidth, drawHeight);
  217. /** Fill the interleaved text after rotation */
  218. ctx.restore();
  219. (0, _utils.rotateWatermark)(ctx, alternateRotateX, alternateRotateY, rotate);
  220. fillTexts(ctx, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
  221. appendWatermark(canvas.toDataURL(), markWidth);
  222. }
  223. }
  224. };
  225. (0, _vue.onMounted)(() => {
  226. renderWatermark();
  227. });
  228. (0, _vue.watch)(() => [props, token.value.colorFill, token.value.fontSizeLG], () => {
  229. renderWatermark();
  230. }, {
  231. deep: true,
  232. flush: 'post'
  233. });
  234. (0, _vue.onBeforeUnmount)(() => {
  235. destroyWatermark();
  236. });
  237. const onMutate = mutations => {
  238. if (stopObservation.value) {
  239. return;
  240. }
  241. mutations.forEach(mutation => {
  242. if ((0, _utils.reRendering)(mutation, watermarkRef.value)) {
  243. destroyWatermark();
  244. renderWatermark();
  245. }
  246. });
  247. };
  248. (0, _useMutationObserver.useMutationObserver)(containerRef, onMutate, {
  249. attributes: true,
  250. subtree: true,
  251. childList: true,
  252. attributeFilter: ['style', 'class']
  253. });
  254. return () => {
  255. var _a;
  256. return (0, _vue.createVNode)("div", (0, _objectSpread2.default)((0, _objectSpread2.default)({}, attrs), {}, {
  257. "ref": containerRef,
  258. "class": [attrs.class, props.rootClassName],
  259. "style": [{
  260. position: 'relative'
  261. }, attrs.style]
  262. }), [(_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots)]);
  263. };
  264. }
  265. });
  266. var _default = exports.default = (0, _type.withInstall)(Watermark);