watermark2.mjs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import { defineComponent, computed, shallowRef, ref, onMounted, watch, onBeforeUnmount, openBlock, createElementBlock, normalizeStyle, renderSlot } from 'vue';
  2. import { useMutationObserver } from '@vueuse/core';
  3. import { watermarkProps } from './watermark.mjs';
  4. import { reRendering, getStyleStr, getPixelRatio } from './utils.mjs';
  5. import useClips from './useClips.mjs';
  6. import _export_sfc from '../../../_virtual/plugin-vue_export-helper.mjs';
  7. import { isArray } from '@vue/shared';
  8. import { isUndefined } from '../../../utils/types.mjs';
  9. const _sfc_main = defineComponent({
  10. ...{
  11. name: "ElWatermark"
  12. },
  13. __name: "watermark",
  14. props: watermarkProps,
  15. setup(__props) {
  16. const style = {
  17. position: "relative"
  18. };
  19. const props = __props;
  20. const fontGap = computed(() => {
  21. var _a, _b;
  22. return (_b = (_a = props.font) == null ? void 0 : _a.fontGap) != null ? _b : 3;
  23. });
  24. const color = computed(() => {
  25. var _a, _b;
  26. return (_b = (_a = props.font) == null ? void 0 : _a.color) != null ? _b : "rgba(0,0,0,.15)";
  27. });
  28. const fontSize = computed(() => {
  29. var _a, _b;
  30. return (_b = (_a = props.font) == null ? void 0 : _a.fontSize) != null ? _b : 16;
  31. });
  32. const fontWeight = computed(() => {
  33. var _a, _b;
  34. return (_b = (_a = props.font) == null ? void 0 : _a.fontWeight) != null ? _b : "normal";
  35. });
  36. const fontStyle = computed(() => {
  37. var _a, _b;
  38. return (_b = (_a = props.font) == null ? void 0 : _a.fontStyle) != null ? _b : "normal";
  39. });
  40. const fontFamily = computed(() => {
  41. var _a, _b;
  42. return (_b = (_a = props.font) == null ? void 0 : _a.fontFamily) != null ? _b : "sans-serif";
  43. });
  44. const textAlign = computed(() => {
  45. var _a, _b;
  46. return (_b = (_a = props.font) == null ? void 0 : _a.textAlign) != null ? _b : "center";
  47. });
  48. const textBaseline = computed(() => {
  49. var _a, _b;
  50. return (_b = (_a = props.font) == null ? void 0 : _a.textBaseline) != null ? _b : "hanging";
  51. });
  52. const gapX = computed(() => props.gap[0]);
  53. const gapY = computed(() => props.gap[1]);
  54. const gapXCenter = computed(() => gapX.value / 2);
  55. const gapYCenter = computed(() => gapY.value / 2);
  56. const offsetLeft = computed(() => {
  57. var _a, _b;
  58. return (_b = (_a = props.offset) == null ? void 0 : _a[0]) != null ? _b : gapXCenter.value;
  59. });
  60. const offsetTop = computed(() => {
  61. var _a, _b;
  62. return (_b = (_a = props.offset) == null ? void 0 : _a[1]) != null ? _b : gapYCenter.value;
  63. });
  64. const getMarkStyle = () => {
  65. const markStyle = {
  66. zIndex: props.zIndex,
  67. position: "absolute",
  68. left: 0,
  69. top: 0,
  70. width: "100%",
  71. height: "100%",
  72. pointerEvents: "none",
  73. backgroundRepeat: "repeat"
  74. };
  75. let positionLeft = offsetLeft.value - gapXCenter.value;
  76. let positionTop = offsetTop.value - gapYCenter.value;
  77. if (positionLeft > 0) {
  78. markStyle.left = `${positionLeft}px`;
  79. markStyle.width = `calc(100% - ${positionLeft}px)`;
  80. positionLeft = 0;
  81. }
  82. if (positionTop > 0) {
  83. markStyle.top = `${positionTop}px`;
  84. markStyle.height = `calc(100% - ${positionTop}px)`;
  85. positionTop = 0;
  86. }
  87. markStyle.backgroundPosition = `${positionLeft}px ${positionTop}px`;
  88. return markStyle;
  89. };
  90. const containerRef = shallowRef(null);
  91. const watermarkRef = shallowRef();
  92. const stopObservation = ref(false);
  93. const destroyWatermark = () => {
  94. if (watermarkRef.value) {
  95. watermarkRef.value.remove();
  96. watermarkRef.value = void 0;
  97. }
  98. };
  99. const appendWatermark = (base64Url, markWidth) => {
  100. var _a;
  101. if (containerRef.value && watermarkRef.value) {
  102. stopObservation.value = true;
  103. watermarkRef.value.setAttribute(
  104. "style",
  105. getStyleStr({
  106. ...getMarkStyle(),
  107. backgroundImage: `url('${base64Url}')`,
  108. backgroundSize: `${Math.floor(markWidth)}px`
  109. })
  110. );
  111. (_a = containerRef.value) == null ? void 0 : _a.append(watermarkRef.value);
  112. setTimeout(() => {
  113. stopObservation.value = false;
  114. });
  115. }
  116. };
  117. const getMarkSize = (ctx) => {
  118. let defaultWidth = 120;
  119. let defaultHeight = 64;
  120. let space = 0;
  121. const { image, content, width, height, rotate } = props;
  122. if (!image && ctx.measureText) {
  123. ctx.font = `${Number(fontSize.value)}px ${fontFamily.value}`;
  124. const contents = isArray(content) ? content : [content];
  125. let maxWidth = 0;
  126. let maxHeight = 0;
  127. contents.forEach((item) => {
  128. const {
  129. width: width2,
  130. fontBoundingBoxAscent,
  131. fontBoundingBoxDescent,
  132. actualBoundingBoxAscent,
  133. actualBoundingBoxDescent
  134. } = ctx.measureText(item);
  135. const height2 = isUndefined(fontBoundingBoxAscent) ? actualBoundingBoxAscent + actualBoundingBoxDescent : fontBoundingBoxAscent + fontBoundingBoxDescent;
  136. if (width2 > maxWidth)
  137. maxWidth = Math.ceil(width2);
  138. if (height2 > maxHeight)
  139. maxHeight = Math.ceil(height2);
  140. });
  141. defaultWidth = maxWidth;
  142. defaultHeight = maxHeight * contents.length + (contents.length - 1) * fontGap.value;
  143. const angle = Math.PI / 180 * Number(rotate);
  144. space = Math.ceil(Math.abs(Math.sin(angle) * defaultHeight) / 2);
  145. defaultWidth += space;
  146. }
  147. return [width != null ? width : defaultWidth, height != null ? height : defaultHeight, space];
  148. };
  149. const getClips = useClips();
  150. const renderWatermark = () => {
  151. const canvas = document.createElement("canvas");
  152. const ctx = canvas.getContext("2d");
  153. const image = props.image;
  154. const content = props.content;
  155. const rotate = props.rotate;
  156. if (ctx) {
  157. if (!watermarkRef.value) {
  158. watermarkRef.value = document.createElement("div");
  159. }
  160. const ratio = getPixelRatio();
  161. const [markWidth, markHeight, space] = getMarkSize(ctx);
  162. const drawCanvas = (drawContent) => {
  163. const [textClips, clipWidth] = getClips(
  164. drawContent || "",
  165. rotate,
  166. ratio,
  167. markWidth,
  168. markHeight,
  169. {
  170. color: color.value,
  171. fontSize: fontSize.value,
  172. fontStyle: fontStyle.value,
  173. fontWeight: fontWeight.value,
  174. fontFamily: fontFamily.value,
  175. fontGap: fontGap.value,
  176. textAlign: textAlign.value,
  177. textBaseline: textBaseline.value
  178. },
  179. gapX.value,
  180. gapY.value,
  181. space
  182. );
  183. appendWatermark(textClips, clipWidth);
  184. };
  185. if (image) {
  186. const img = new Image();
  187. img.onload = () => {
  188. drawCanvas(img);
  189. };
  190. img.onerror = () => {
  191. drawCanvas(content);
  192. };
  193. img.crossOrigin = "anonymous";
  194. img.referrerPolicy = "no-referrer";
  195. img.src = image;
  196. } else {
  197. drawCanvas(content);
  198. }
  199. }
  200. };
  201. onMounted(() => {
  202. renderWatermark();
  203. });
  204. watch(
  205. () => props,
  206. () => {
  207. renderWatermark();
  208. },
  209. {
  210. deep: true,
  211. flush: "post"
  212. }
  213. );
  214. onBeforeUnmount(() => {
  215. destroyWatermark();
  216. });
  217. const onMutate = (mutations) => {
  218. if (stopObservation.value) {
  219. return;
  220. }
  221. mutations.forEach((mutation) => {
  222. if (reRendering(mutation, watermarkRef.value)) {
  223. destroyWatermark();
  224. renderWatermark();
  225. }
  226. });
  227. };
  228. useMutationObserver(containerRef, onMutate, {
  229. attributes: true,
  230. subtree: true,
  231. childList: true
  232. });
  233. return (_ctx, _cache) => {
  234. return openBlock(), createElementBlock(
  235. "div",
  236. {
  237. ref_key: "containerRef",
  238. ref: containerRef,
  239. style: normalizeStyle([style])
  240. },
  241. [
  242. renderSlot(_ctx.$slots, "default")
  243. ],
  244. 4
  245. );
  246. };
  247. }
  248. });
  249. var Watermark = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "/home/runner/work/element-plus/element-plus/packages/components/watermark/src/watermark.vue"]]);
  250. export { Watermark as default };
  251. //# sourceMappingURL=watermark2.mjs.map