From e43edd4739947500752ecb9ed6afc416469594b2 Mon Sep 17 00:00:00 2001 From: ClariS <1457715339@qq.com> Date: Wed, 30 Aug 2023 15:32:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=B0=B4=E5=8D=B0=20?= =?UTF-8?q?hook=20(#125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useWatermark.ts | 144 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/hooks/useWatermark.ts diff --git a/src/hooks/useWatermark.ts b/src/hooks/useWatermark.ts new file mode 100644 index 0000000..a3577d5 --- /dev/null +++ b/src/hooks/useWatermark.ts @@ -0,0 +1,144 @@ +import { throttle } from "lodash-es" +import { Ref, getCurrentInstance, onBeforeUnmount, ref, shallowRef } from "vue" + +const defaultConfig = { + /** 文本颜色 */ + textColor: "#000", + /** 文本透明度 */ + textOpacity: 0.2, + /** 文本字体大小 */ + textSize: "16px", + /** 文本字体 */ + fontFamily: "serif", + /** 倾斜角度 */ + angle: -20, + /** canvas 宽度 */ + width: 180, + /** canvas 高度 */ + height: 100 +} + +type TargetNode = T & { _observer?: MutationObserver } + +export function useWatermark(wrapperEl: Ref = ref(document.body)) { + const watermarkEl = shallowRef() + const newConfig = ref() + + const clear = () => { + if (!wrapperEl.value || !watermarkEl.value) return + wrapperEl.value.removeChild(watermarkEl.value) + watermarkEl.value = null + // 移除对水印容器的监听 + removeResizeListener(wrapperEl.value) + } + + const createBase64 = (text: string) => { + const { width, height, angle, textSize, textColor, textOpacity } = newConfig.value ?? defaultConfig + const canvasEl = document.createElement("canvas") + canvasEl.width = width + canvasEl.height = height + const ctx = canvasEl.getContext("2d") + if (ctx) { + ctx.clearRect(0, 0, width, height) + ctx.rotate((Math.PI / 180) * angle) + ctx.font = `${textSize} serif` + ctx.fillStyle = textColor + ctx.globalAlpha = textOpacity + ctx.fillText(text, 0, height / 2) + } + return canvasEl.toDataURL() + } + + function updateWatermark( + options: { + width?: number + height?: number + text?: string + } = {} + ) { + if (!watermarkEl.value) return + options.width && (watermarkEl.value.style.width = `${options.width}px`) + options.height && (watermarkEl.value.style.height = `${options.height}px`) + options.text && (watermarkEl.value.style.background = `url(${createBase64(options.text)}) left top repeat`) + } + + const createWatermark = (text: string) => { + if (!wrapperEl.value) return + if (watermarkEl.value) { + updateWatermark({ text }) + return + } + const div = document.createElement("div") + watermarkEl.value = div + div.style.pointerEvents = "none" + div.style.top = "0px" + div.style.left = "0px" + div.style.position = "absolute" + div.style.zIndex = "100000" + const { clientWidth, clientHeight } = wrapperEl.value + updateWatermark({ text, width: clientWidth, height: clientHeight }) + wrapperEl.value.appendChild(div) + return + } + + const setWatermark = (text: string, options: Partial = {}) => { + if (!wrapperEl.value) return + // 设置水印容器为相对定位 + wrapperEl.value.style.position = "relative" + // 合并配置 + newConfig.value = { ...defaultConfig, ...options } + // 创建水印 + createWatermark(text) + // 监听水印容器变化 + addResizeListener(wrapperEl.value) + // 当前组件实例卸载前删除水印 + getCurrentInstance() && onBeforeUnmount(clear) + } + + const defendRemoveWatermark = (mutationList: MutationRecord[], targetNode: HTMLElement) => { + mutationList.forEach((mutation) => { + switch (mutation.type) { + case "childList": + mutation.removedNodes.forEach((item) => { + item === watermarkEl.value && targetNode.appendChild(watermarkEl.value) + }) + break + case "attributes": + if (watermarkEl.value!.style.display === "none") { + watermarkEl.value!.style.display = "block" + } + break + } + }) + } + + const addResizeListener = (targetNode: TargetNode) => { + if (targetNode._observer || !watermarkEl.value) return + // 观察器的配置(需要观察什么变动) + const observerOptions: MutationObserverInit = { + // 观察目标子节点的变化,是否有添加或者删除 + attributes: true, + // 观察属性变动 + childList: true + } + // 当观察到变动时执行的回调 + const callback = throttle((mutationList: MutationRecord[]) => { + const { clientWidth, clientHeight } = targetNode + updateWatermark({ width: clientWidth, height: clientHeight }) + // 明水印的防御 + defendRemoveWatermark(mutationList, targetNode) + }, 500) + // 创建一个观察器实例并传入回调 + const observer = new MutationObserver(callback) + // 以上述配置开始观察目标节点 + observer.observe(targetNode, observerOptions) + targetNode._observer = observer + } + + const removeResizeListener = (targetNode: TargetNode) => { + targetNode._observer?.disconnect() + targetNode._observer = undefined + } + + return { setWatermark, clear } +}