From 9dfa701454d6a94690bad39dbb0e38f2a0b31489 Mon Sep 17 00:00:00 2001
From: Ben Lin <maobin001@msn.com>
Date: 星期二, 18 六月 2024 18:08:47 +0800
Subject: [PATCH] build

---
 src/hooks/web/useWatermark.ts |  185 ++++++++++++++++++++++++++++++++++++----------
 1 files changed, 144 insertions(+), 41 deletions(-)

diff --git a/src/hooks/web/useWatermark.ts b/src/hooks/web/useWatermark.ts
index 5840c14..671cedf 100644
--- a/src/hooks/web/useWatermark.ts
+++ b/src/hooks/web/useWatermark.ts
@@ -1,17 +1,123 @@
 import { getCurrentInstance, onBeforeUnmount, ref, Ref, shallowRef, unref } from 'vue';
-import { useRafThrottle } from '/@/utils/domUtils';
-import { addResizeListener, removeResizeListener } from '/@/utils/event';
-import { isDef } from '/@/utils/is';
+import { useRafThrottle } from '@/utils/domUtils';
+import { addResizeListener, removeResizeListener } from '@/utils/event';
+import { isDef } from '@/utils/is';
 
-const domSymbol = Symbol('watermark-dom');
-const sourceMap = new WeakMap<HTMLElement, {}>();
+const watermarkSymbol = 'watermark-dom';
+const updateWatermarkText = ref<string | null>(null);
+
+type UseWatermarkRes = {
+  setWatermark: (str: string) => void;
+  clear: () => void;
+  clearAll: () => void;
+  waterMarkOptions?: waterMarkOptionsType;
+  obInstance?: MutationObserver;
+  targetElement?: HTMLElement;
+  parentElement?: HTMLElement;
+};
+
+type waterMarkOptionsType = {
+  // 鑷畾涔夋按鍗扮殑鏂囧瓧澶у皬
+  fontSize?: number;
+  // 鑷畾涔夋按鍗扮殑鏂囧瓧棰滆壊
+  fontColor?: string;
+  // 鑷畾涔夋按鍗扮殑鏂囧瓧瀛椾綋
+  fontFamily?: string;
+  // 鑷畾涔夋按鍗扮殑鏂囧瓧瀵归綈鏂瑰紡
+  textAlign?: CanvasTextAlign;
+  // 鑷畾涔夋按鍗扮殑鏂囧瓧鍩虹嚎
+  textBaseline?: CanvasTextBaseline;
+  // 鑷畾涔夋按鍗扮殑鏂囧瓧鍊炬枩瑙掑害
+  rotate?: number;
+};
+
+const sourceMap = new Map<Symbol, Omit<UseWatermarkRes, 'clearAll'>>();
+
+function findTargetNode(el) {
+  return Array.from(sourceMap.values()).find((item) => item.targetElement === el);
+}
+
+function createBase64(str: string, waterMarkOptions: waterMarkOptionsType) {
+  const can = document.createElement('canvas');
+  const width = 300;
+  const height = 240;
+  Object.assign(can, { width, height });
+
+  const cans = can.getContext('2d');
+  if (cans) {
+    const fontFamily = waterMarkOptions?.fontFamily || 'Vedana';
+    const fontSize = waterMarkOptions?.fontSize || 15;
+    const fontColor = waterMarkOptions?.fontColor || 'rgba(0, 0, 0, 0.15)';
+    const textAlign = waterMarkOptions?.textAlign || 'left';
+    const textBaseline = waterMarkOptions?.textBaseline || 'middle';
+    const rotate = waterMarkOptions?.rotate || 20;
+    cans.rotate((-rotate * Math.PI) / 180);
+    cans.font = `${fontSize}px ${fontFamily}`;
+    cans.fillStyle = fontColor;
+    cans.textAlign = textAlign;
+    cans.textBaseline = textBaseline;
+    cans.fillText(str, width / 20, height);
+  }
+  return can.toDataURL('image/png');
+}
+const resetWatermarkStyle = (
+  element: HTMLElement,
+  watermarkText: string,
+  waterMarkOptions: waterMarkOptionsType,
+) => {
+  element.className = '__' + watermarkSymbol;
+  element.style.pointerEvents = 'none';
+  element.style.display = 'block';
+  element.style.visibility = 'visible';
+  element.style.top = '0px';
+  element.style.left = '0px';
+  element.style.position = 'absolute';
+  element.style.zIndex = '100000';
+  element.style.height = '100%';
+  element.style.width = '100%';
+  element.style.background = `url(${createBase64(
+    unref(updateWatermarkText) || watermarkText,
+    waterMarkOptions,
+  )}) left top repeat`;
+};
+
+const obFn = () => {
+  const obInstance = new MutationObserver((mutationRecords) => {
+    for (const mutation of mutationRecords) {
+      for (const node of Array.from(mutation.removedNodes)) {
+        const target = findTargetNode(node);
+        if (!target) return;
+        const { targetElement, parentElement } = target;
+        // 鐖跺厓绱犵殑瀛愬厓绱犳按鍗板鏋滆鍒犻櫎 閲嶆柊鎻掑叆琚垹闄ょ殑姘村嵃(闃茬鏀癸紝鎻掑叆閫氳繃鎺у埗鍙板垹闄ょ殑姘村嵃)
+        if (!parentElement?.contains(targetElement as Node | null)) {
+          target?.parentElement?.appendChild(node as HTMLElement);
+        }
+      }
+      if (mutation.type === 'attributes' && mutation.target) {
+        // 淇鎺у埗鍙板彲浠モ�滺ide element鈥� 鐨勯棶棰�
+        const _target = mutation.target as HTMLElement;
+        const target = findTargetNode(_target);
+        if (target) {
+          // 绂佹鏀瑰睘鎬� 鍖呮嫭class 淇敼浠ュ悗 mutation.type 涔熺瓑浜� 'attributes'
+          // 鍏堣В闄ょ洃鍚� 鍐嶅姞涓�涓�
+          clearAll();
+          target.setWatermark(target.targetElement?.['data-watermark-text']);
+        }
+      }
+    }
+  });
+  return obInstance;
+};
 
 export function useWatermark(
   appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement>,
-) {
+  waterMarkOptions: waterMarkOptionsType = {},
+): UseWatermarkRes {
+  const domSymbol = Symbol(watermarkSymbol);
   const appendElRaw = unref(appendEl);
-  if (appendElRaw && sourceMap.has(appendElRaw)) {
-    return sourceMap.get(appendElRaw);
+  if (appendElRaw && sourceMap.has(domSymbol)) {
+    const { setWatermark, clear } = sourceMap.get(domSymbol) as UseWatermarkRes;
+    return { setWatermark, clear, clearAll };
   }
   const func = useRafThrottle(function () {
     const el = unref(appendEl);
@@ -19,35 +125,17 @@
     const { clientHeight: height, clientWidth: width } = el;
     updateWatermark({ height, width });
   });
-  const id = domSymbol.toString();
   const watermarkEl = shallowRef<HTMLElement>();
-
   const clear = () => {
     const domId = unref(watermarkEl);
     watermarkEl.value = undefined;
     const el = unref(appendEl);
+    sourceMap.has(domSymbol) && sourceMap.get(domSymbol)?.obInstance?.disconnect();
+    sourceMap.delete(domSymbol);
     if (!el) return;
     domId && el.removeChild(domId);
     removeResizeListener(el, func);
   };
-
-  function createBase64(str: string) {
-    const can = document.createElement('canvas');
-    const width = 300;
-    const height = 240;
-    Object.assign(can, { width, height });
-
-    const cans = can.getContext('2d');
-    if (cans) {
-      cans.rotate((-20 * Math.PI) / 120);
-      cans.font = '15px Vedana';
-      cans.fillStyle = 'rgba(0, 0, 0, 0.15)';
-      cans.textAlign = 'left';
-      cans.textBaseline = 'middle';
-      cans.fillText(str, width / 20, height);
-    }
-    return can.toDataURL('image/png');
-  }
 
   function updateWatermark(
     options: {
@@ -65,30 +153,39 @@
       el.style.height = `${options.height}px`;
     }
     if (isDef(options.str)) {
-      el.style.background = `url(${createBase64(options.str)}) left top repeat`;
+      el.style.background = `url(${createBase64(options.str, waterMarkOptions)}) left top repeat`;
     }
   }
 
   const createWatermark = (str: string) => {
-    if (unref(watermarkEl)) {
+    if (unref(watermarkEl) && sourceMap.has(domSymbol)) {
+      updateWatermarkText.value = str;
       updateWatermark({ str });
-      return id;
+      return;
     }
     const div = document.createElement('div');
+    div['data-watermark-text'] = str; //鑷畾涔夊睘鎬� 鐢ㄤ簬鎭㈠姘村嵃
+    updateWatermarkText.value = str;
     watermarkEl.value = div;
-    div.id = id;
-    div.style.pointerEvents = 'none';
-    div.style.top = '0px';
-    div.style.left = '0px';
-    div.style.position = 'absolute';
-    div.style.zIndex = '100000';
+    resetWatermarkStyle(div, str, waterMarkOptions);
     const el = unref(appendEl);
-    if (!el) return id;
+    if (!el) return;
     const { clientHeight: height, clientWidth: width } = el;
     updateWatermark({ str, width, height });
     el.appendChild(div);
-    sourceMap.set(el, { setWatermark, clear });
-    return id;
+    sourceMap.set(domSymbol, {
+      setWatermark,
+      clear,
+      parentElement: el,
+      targetElement: div,
+      obInstance: obFn(),
+      waterMarkOptions,
+    });
+    sourceMap.get(domSymbol)?.obInstance?.observe(el, {
+      childList: true, // 瀛愯妭鐐圭殑鍙樺姩锛堟寚鏂板锛屽垹闄ゆ垨鑰呮洿鏀癸級
+      subtree: true, // 璇ヨ瀵熷櫒搴旂敤浜庤鑺傜偣鐨勬墍鏈夊悗浠h妭鐐�
+      attributes: true, // 灞炴�х殑鍙樺姩
+    });
   };
 
   function setWatermark(str: string) {
@@ -101,6 +198,12 @@
       });
     }
   }
+  return { setWatermark, clear, clearAll };
+}
 
-  return { setWatermark, clear };
+function clearAll() {
+  Array.from(sourceMap.values()).forEach((item) => {
+    item?.obInstance?.disconnect();
+    item.clear();
+  });
 }

--
Gitblit v1.9.3