watermark2.mjs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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 __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 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("style", getStyleStr({
  104. ...getMarkStyle(),
  105. backgroundImage: `url('${base64Url}')`,
  106. backgroundSize: `${Math.floor(markWidth)}px`
  107. }));
  108. (_a = containerRef.value) == null ? void 0 : _a.append(watermarkRef.value);
  109. setTimeout(() => {
  110. stopObservation.value = false;
  111. });
  112. }
  113. };
  114. const getMarkSize = (ctx) => {
  115. let defaultWidth = 120;
  116. let defaultHeight = 64;
  117. let space = 0;
  118. const { image, content, width, height, rotate } = props;
  119. if (!image && ctx.measureText) {
  120. ctx.font = `${Number(fontSize.value)}px ${fontFamily.value}`;
  121. const contents = isArray(content) ? content : [content];
  122. let maxWidth = 0;
  123. let maxHeight = 0;
  124. contents.forEach((item) => {
  125. const {
  126. width: width2,
  127. fontBoundingBoxAscent,
  128. fontBoundingBoxDescent,
  129. actualBoundingBoxAscent,
  130. actualBoundingBoxDescent
  131. } = ctx.measureText(item);
  132. const height2 = isUndefined(fontBoundingBoxAscent) ? actualBoundingBoxAscent + actualBoundingBoxDescent : fontBoundingBoxAscent + fontBoundingBoxDescent;
  133. if (width2 > maxWidth)
  134. maxWidth = Math.ceil(width2);
  135. if (height2 > maxHeight)
  136. maxHeight = Math.ceil(height2);
  137. });
  138. defaultWidth = maxWidth;
  139. defaultHeight = maxHeight * contents.length + (contents.length - 1) * fontGap.value;
  140. const angle = Math.PI / 180 * Number(rotate);
  141. space = Math.ceil(Math.abs(Math.sin(angle) * defaultHeight) / 2);
  142. defaultWidth += space;
  143. }
  144. return [width != null ? width : defaultWidth, height != null ? height : defaultHeight, space];
  145. };
  146. const getClips = useClips();
  147. const renderWatermark = () => {
  148. const canvas = document.createElement("canvas");
  149. const ctx = canvas.getContext("2d");
  150. const image = props.image;
  151. const content = props.content;
  152. const rotate = props.rotate;
  153. if (ctx) {
  154. if (!watermarkRef.value) {
  155. watermarkRef.value = document.createElement("div");
  156. }
  157. const ratio = getPixelRatio();
  158. const [markWidth, markHeight, space] = getMarkSize(ctx);
  159. const drawCanvas = (drawContent) => {
  160. const [textClips, clipWidth] = getClips(drawContent || "", rotate, ratio, markWidth, markHeight, {
  161. color: color.value,
  162. fontSize: fontSize.value,
  163. fontStyle: fontStyle.value,
  164. fontWeight: fontWeight.value,
  165. fontFamily: fontFamily.value,
  166. fontGap: fontGap.value,
  167. textAlign: textAlign.value,
  168. textBaseline: textBaseline.value
  169. }, gapX.value, gapY.value, space);
  170. appendWatermark(textClips, clipWidth);
  171. };
  172. if (image) {
  173. const img = new Image();
  174. img.onload = () => {
  175. drawCanvas(img);
  176. };
  177. img.onerror = () => {
  178. drawCanvas(content);
  179. };
  180. img.crossOrigin = "anonymous";
  181. img.referrerPolicy = "no-referrer";
  182. img.src = image;
  183. } else {
  184. drawCanvas(content);
  185. }
  186. }
  187. };
  188. onMounted(() => {
  189. renderWatermark();
  190. });
  191. watch(() => props, () => {
  192. renderWatermark();
  193. }, {
  194. deep: true,
  195. flush: "post"
  196. });
  197. onBeforeUnmount(() => {
  198. destroyWatermark();
  199. });
  200. const onMutate = (mutations) => {
  201. if (stopObservation.value) {
  202. return;
  203. }
  204. mutations.forEach((mutation) => {
  205. if (reRendering(mutation, watermarkRef.value)) {
  206. destroyWatermark();
  207. renderWatermark();
  208. }
  209. });
  210. };
  211. useMutationObserver(containerRef, onMutate, {
  212. attributes: true,
  213. subtree: true,
  214. childList: true
  215. });
  216. return (_ctx, _cache) => {
  217. return openBlock(), createElementBlock("div", {
  218. ref_key: "containerRef",
  219. ref: containerRef,
  220. style: normalizeStyle([style])
  221. }, [
  222. renderSlot(_ctx.$slots, "default")
  223. ], 4);
  224. };
  225. }
  226. });
  227. var Watermark = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "watermark.vue"]]);
  228. export { Watermark as default };
  229. //# sourceMappingURL=watermark2.mjs.map