wip: 水印 hook 优化

This commit is contained in:
pany 2023-08-30 18:26:49 +08:00
parent e43edd4739
commit 2c7d5b2b76
4 changed files with 96 additions and 46 deletions

View File

@ -1,54 +1,63 @@
import { type Ref, getCurrentInstance, onBeforeUnmount, ref, shallowRef } from "vue"
import { throttle } from "lodash-es" 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> = T & { _observer?: MutationObserver } type TargetNode<T> = T & { _observer?: MutationObserver }
export function useWatermark(wrapperEl: Ref<HTMLElement | null> = ref(document.body)) { type DefaultConfig = typeof defaultConfig
const watermarkEl = shallowRef<HTMLElement | null>()
const newConfig = ref<typeof defaultConfig | null>()
const clear = () => { /** 默认配置 */
if (!wrapperEl.value || !watermarkEl.value) return const defaultConfig = {
wrapperEl.value.removeChild(watermarkEl.value) /** 文本颜色 */
watermarkEl.value = null color: "#c0c4cc",
// 移除对水印容器的监听 /** 文本透明度 */
removeResizeListener(wrapperEl.value) opacity: 0.5,
} /** 文本字体大小 */
size: "16px",
/** 文本字体 */
family: "serif",
/** 文本倾斜角度 */
angle: -20,
/** canvas 宽度 */
width: 200,
/** canvas 高度 */
height: 150
}
/** body 元素 */
const bodyEl = ref<HTMLElement>(document.body)
export function useWatermark(parentEl: Ref<HTMLElement | null> = bodyEl) {
/** 最终配置 */
let mergeConfig: DefaultConfig
/** 水印元素 */
const watermarkEl = shallowRef<HTMLElement | null>(null)
const createBase64 = (text: string) => { const createBase64 = (text: string) => {
const { width, height, angle, textSize, textColor, textOpacity } = newConfig.value ?? defaultConfig const { color, opacity, size, family, angle, width, height } = mergeConfig ?? defaultConfig
const canvasEl = document.createElement("canvas") const canvasEl = document.createElement("canvas")
canvasEl.width = width canvasEl.width = width
canvasEl.height = height canvasEl.height = height
const ctx = canvasEl.getContext("2d") const ctx = canvasEl.getContext("2d")
if (ctx) { if (ctx) {
ctx.clearRect(0, 0, width, height) ctx.fillStyle = color
ctx.globalAlpha = opacity
ctx.font = size + " " + family
ctx.rotate((Math.PI / 180) * angle) ctx.rotate((Math.PI / 180) * angle)
ctx.font = `${textSize} serif` ctx.clearRect(0, 0, width, height)
ctx.fillStyle = textColor
ctx.globalAlpha = textOpacity
ctx.fillText(text, 0, height / 2) ctx.fillText(text, 0, height / 2)
} }
return canvasEl.toDataURL() return canvasEl.toDataURL()
} }
/** 清除水印 */
const clear = () => {
if (!parentEl.value || !watermarkEl.value) return
parentEl.value.removeChild(watermarkEl.value)
watermarkEl.value = null
// 移除对水印容器的监听
removeResizeListener(parentEl.value)
}
function updateWatermark( function updateWatermark(
options: { options: {
width?: number width?: number
@ -63,7 +72,7 @@ export function useWatermark(wrapperEl: Ref<HTMLElement | null> = ref(document.b
} }
const createWatermark = (text: string) => { const createWatermark = (text: string) => {
if (!wrapperEl.value) return if (!parentEl.value) return
if (watermarkEl.value) { if (watermarkEl.value) {
updateWatermark({ text }) updateWatermark({ text })
return return
@ -71,26 +80,25 @@ export function useWatermark(wrapperEl: Ref<HTMLElement | null> = ref(document.b
const div = document.createElement("div") const div = document.createElement("div")
watermarkEl.value = div watermarkEl.value = div
div.style.pointerEvents = "none" div.style.pointerEvents = "none"
div.style.top = "0px" div.style.top = "0"
div.style.left = "0px" div.style.left = "0"
div.style.position = "absolute" div.style.position = "absolute"
div.style.zIndex = "100000" div.style.zIndex = "100000"
const { clientWidth, clientHeight } = wrapperEl.value const { clientWidth, clientHeight } = parentEl.value
updateWatermark({ text, width: clientWidth, height: clientHeight }) updateWatermark({ width: clientWidth, height: clientHeight, text })
wrapperEl.value.appendChild(div) parentEl.value.appendChild(div)
return
} }
const setWatermark = (text: string, options: Partial<typeof defaultConfig> = {}) => { const setWatermark = (text: string, config: Partial<DefaultConfig> = {}) => {
if (!wrapperEl.value) return if (!parentEl.value) return
// 设置水印容器为相对定位 // 设置水印容器为相对定位
wrapperEl.value.style.position = "relative" parentEl.value.style.position = "relative"
// 合并配置 // 合并配置
newConfig.value = { ...defaultConfig, ...options } mergeConfig = { ...defaultConfig, ...config }
// 创建水印 // 创建水印
createWatermark(text) createWatermark(text)
// 监听水印容器变化 // 监听水印容器变化
addResizeListener(wrapperEl.value) addResizeListener(parentEl.value)
// 当前组件实例卸载前删除水印 // 当前组件实例卸载前删除水印
getCurrentInstance() && onBeforeUnmount(clear) getCurrentInstance() && onBeforeUnmount(clear)
} }

View File

@ -32,7 +32,7 @@ const show = ref(false)
background-color: var(--v3-rightpanel-button-bg-color); background-color: var(--v3-rightpanel-button-bg-color);
position: fixed; position: fixed;
top: v-bind(buttonTopCss); top: v-bind(buttonTopCss);
right: 0px; right: 0;
border-radius: 6px 0 0 6px; border-radius: 6px 0 0 6px;
z-index: 10; z-index: 10;
cursor: pointer; cursor: pointer;

View File

@ -240,6 +240,14 @@ export const constantRoutes: RouteRecordRaw[] = [
meta: { meta: {
title: "useFullscreenLoading" title: "useFullscreenLoading"
} }
},
{
path: "use-watermark",
component: () => import("@/views/hook-demo/use-watermark.vue"),
name: "UseWatermark",
meta: {
title: "useWatermark"
}
} }
] ]
} }

View File

@ -0,0 +1,34 @@
<script setup lang="ts">
import { ref } from "vue"
import { useWatermark } from "@/hooks/useWatermark"
const localRef = ref<HTMLElement | null>(null)
const { setWatermark, clear } = useWatermark(localRef)
const { setWatermark: setGlobalWatermark, clear: clearGlobalWatermark } = useWatermark()
</script>
<template>
<div class="app-container">
<div ref="localRef" class="local" />
<el-button-group>
<el-button type="primary" @click="setWatermark('局部水印', { color: '#409eff' })">创建局部水印</el-button>
<el-button type="primary" @click="clear">清除局部水印</el-button>
</el-button-group>
<el-button-group>
<el-button type="primary" @click="setGlobalWatermark('全局水印')">创建全局水印</el-button>
<el-button type="primary" @click="clearGlobalWatermark">清除全局水印</el-button>
</el-button-group>
</div>
</template>
<style lang="scss" scoped>
.local {
height: 30vh;
border: 2px dashed var(--el-color-primary);
margin-bottom: 20px;
}
.el-button-group {
margin-right: 10px;
}
</style>