608ee76c56e7910b124c1d51b20e8fa15d43bca62af53fb9400802d4171da1d02d67e34f22c8804bd0775ab8798d0008e711739581e9cd1d15f5ba191223f3 7.8 KB

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