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