我发现element-plus中的水印(Watermark)组件我在浏览器面板中去删除dom元素或者设置css属性将其隐藏掉,发现不可行!这是怎么实现的呢?原来是使用了MutationObserver这个API。我这使用了TailwindCss和Vue3、TS进行举例。
标签布局
//容器元素
<div class="h-full overflow-hidden" ref="watermarkRef"></div>
生成base64图片
<script lang="ts" setup>
interface WatermarkInfo {
text: string,
width?: number
height?: number
}
const watermarkinfo = reactive({
text: 'FCV',
})
const useWatermarkBg = (props: WatermarkInfo) => {
const { text } = props
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (!ctx) return ''
// 获取文字宽度
const textMetrics = ctx.measureText(text)
const fontWidth = textMetrics.width
// 设置画布宽高
canvas.width = 3 * fontWidth
// 设置画布高度
canvas.height = 3 * fontWidth
// 设置字体大小
const fontsize = canvas.width / (text.length + 2)
// 设置字体
ctx.font = `${fontsize}px "Microsoft YaHei", "PingFang SC", sans-serif`
// 设置填充颜色
ctx.fillStyle = `#3b82f6`
// 设置文字水平居中
ctx.textAlign = 'center'
// 设置文字垂直居中
ctx.textBaseline = 'middle'
// 设置旋转角度
ctx.rotate((10 * Math.PI) / 180)
// 文字中心绘制
ctx.fillText(text, canvas.width / 2, canvas.width / 2)
// 转为base64
return canvas.toDataURL()
}
</script>
重置水印元素
<script lang='ts' setup>
// 获取父元素
const watermarkRef = useTemplateRef('watermarkRef')
let div: HTMLDivElement
// 重置防止篡改
function resetWatermark() {
if (!watermarkRef.value) return
if (div) {
// 删除之前的div
div.remove()
}
// 获取base64
const base64Url = useWatermarkBg(watermarkinfo)
// 重新创建新的div并添加到watermarkRef元素中
div = document.createElement('div')
// 设置背景图片
div.style.background = `url(${base64Url}) repeat 0 0`
// 设置不可点击
div.style.pointerEvents = 'none'
div.style.width = '100%'
div.style.height = '100%'
// 添加到父元素
watermarkRef.value.appendChild(div)
}
</script>
监听元素变化
当需要监测 DOM 的变化(如节点的增减、属性的修改、文本内容的变动等)时,MutationObserver是一个非常实用的 API。为开发者提供了一种异步观察 DOM 树结构变化的能力,能够高效地响应 DOM 的动态修改。
<script lang='ts' setup>
const ob = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
for (const node of mutation.removedNodes) {
// 判断删除中的节点是否是添加水印的节点,是的话重新生成新的节点
if (node === div) {
resetWatermark()
return
}
}
// 当修改属性时,mutation.target就是修改的元素,所以需要判断
if (mutation.target === div) {
resetWatermark()
}
}
})
onMounted(() => {
resetWatermark()
ob.observe(watermarkRef.value!, {
//监听元素属性变化
attributes: true,
//监听子元素变化
childList: true,
//监听子树变化
subtree: true
})
})
onUnmounted(() => {
// 组件卸载停止监听
ob.disconnect()
})
</script>
完整代码
<script lang='ts' setup>
interface WatermarkInfo {
text: string,
width?: number
height?: number
}
const watermarkinfo = reactive({
text: 'FCV',
})
// 获取父元素
const watermarkRef = useTemplateRef('watermarkRef')
const useWatermarkBg = (props: WatermarkInfo) => {
const { text } = props
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (!ctx) return ''
// 获取文字宽度
const textMetrics = ctx.measureText(text)
const fontWidth = textMetrics.width
// 设置画布宽高
canvas.width = 3 * fontWidth
// 设置画布高度
canvas.height = 3 * fontWidth
// 设置字体大小
const fontsize = canvas.width / (text.length + 2)
// 设置字体
ctx.font = `${fontsize}px "Microsoft YaHei", "PingFang SC", sans-serif`
// 设置填充颜色
ctx.fillStyle = `#3b82f6`
// 设置文字水平居中
ctx.textAlign = 'center'
// 设置文字垂直居中
ctx.textBaseline = 'middle'
// 设置旋转角度
ctx.rotate((10 * Math.PI) / 180)
// 文字中心绘制
ctx.fillText(text, canvas.width / 2, canvas.width / 2)
// 转为base64
return canvas.toDataURL()
}
let div: HTMLDivElement
// 重置防止篡改
function resetWatermark() {
if (!watermarkRef.value || !watermarkinfo.text) return
if (div) {
// 删除之前的div
div.remove()
}
// 获取base64
const base64Url = useWatermarkBg(watermarkinfo)
// 重新创建新的div并添加到watermarkRef元素中
div = document.createElement('div')
// 设置背景图片
div.style.background = `url(${base64Url}) repeat 0 0`
// 设置不可点击
div.style.pointerEvents = 'none'
div.style.width = '100%'
div.style.height = '100%'
// 添加到父元素
watermarkRef.value.appendChild(div)
}
const ob = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
for (const node of mutation.removedNodes) {
// 判断删除中的节点是否是添加水印的节点,是的话重新生成新的节点
if (node === div) {
resetWatermark()
return
}
}
// 当修改属性时,mutation.target就是修改的元素,所以需要判断
if (mutation.target === div) {
resetWatermark()
}
}
})
function reset() {
watermarkinfo.text = 'FCV'
resetWatermark()
}
function download() {
const base64Url = useWatermarkBg(watermarkinfo)
const a = document.createElement('a')
a.href = base64Url
a.download = 'watermark.webp'
a.click()
}
onMounted(() => {
resetWatermark()
ob.observe(watermarkRef.value!, {
//监听元素属性变化
attributes: true,
//监听子元素变化
childList: true,
//监听子树变化
subtree: true
})
})
onUnmounted(() => {
// 组件卸载停止监听
ob.disconnect()
})
</script>
<template>
<div class="h-full">
<div class="h-full overflow-hidden" ref="watermarkRef"></div>
</div>
</template>
<style scoped lang='scss'></style>
欢迎评论区留言!
812

被折叠的 条评论
为什么被折叠?



