wip: 水印 hook 优化
This commit is contained in:
parent
e43edd4739
commit
2c7d5b2b76
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
34
src/views/hook-demo/use-watermark.vue
Normal file
34
src/views/hook-demo/use-watermark.vue
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user