总有老六能通过控制台右键水印元素并轻轻地点一下“Delete Element”便让前端水印不复存在肿么办?
基于Vue3实现的自定义指令 v-watermark
通过MutationObserver监听水印元素的操作,一旦水印元素被删除或者样式属性被更改,就重新执行一遍生成水印的逻辑
以下所有配置字段均为可选
| 字段名称 | 字段值类型 | 描述 |
|---|---|---|
| font | string | 水印字体大小、类型 |
| textColor | string | 水印字体颜色 |
| text | string | 水印文本 |
| textWidth | number | 水印文本宽度 |
| textHeight | number | 水印文本高度 |
| textRotate | number | 水印字体的旋转角度 |
| canvasWidth | number | 控制渲染的水印列数 |
| canvasHeight | number | 控制渲染的水印行数 |
<template>
<header>
<div class="wrapper" v-watermark="waterMarkInfo">
<img src="./assets/logo.svg" class="wrapper-img" alt="vue-logo" />
</div>
</header>
<RouterView />
</template>
<script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。
<script setup lang="ts">
import { RouterView } from 'vue-router'
import type { Directive } from 'vue'
type TValue = {
font?: string
textColor?: string
text?: string
textWidth?: number
textHeight?: number
textRotate?: number
canvasWidth?: number
canvasHeight?: number
}
const waterMarkId = 'notTumbler-WaterMark'
const canvasId = 'notTumbler-Canvas'
// 水印相关设置
const waterMarkInfo = {
font: '30px JhengHei',
text: '水印',
textColor: 'rgba(180, 180, 180, 0.4)',
textWidth: 40,
textHeight: 50,
textRotate: -20,
canvasWidth: 200,
canvasHeight: 270,
}
const drawWatermark = (el: HTMLBaseElement, value: TValue) => {
const { width: elWidth, height: elHeight } = el.getBoundingClientRect()
const {
font = '16px Microsoft JhengHei',
textColor = 'rgba(180, 180, 180, 0.3)',
text = 'notTumbler',
textWidth = 40,
textHeight = 50,
canvasWidth = 200,
canvasHeight = 240,
textRotate = -20,
} = value
// 创建一个canvas标签
const canvasTag = document.getElementById(canvasId) as HTMLCanvasElement
const canvas = canvasTag || document.createElement('canvas')
canvas.id = canvasId
el.appendChild(canvas)
// 设置宽高
canvas.width = canvasWidth
canvas.height = canvasHeight
canvas.style.display = 'none'
const ctx = canvas.getContext('2d')!
// 画布相关设置
ctx.rotate((textRotate * Math.PI) / 180)
ctx.font = font
ctx.fillStyle = textColor
ctx.textAlign = 'left'
ctx.textBaseline = 'middle'
ctx.fillText(text, textWidth, textHeight)
// 水印容器
const waterMaskDiv = document.createElement('div')
waterMaskDiv.id = waterMarkId
// 设置容器的属性样式
// 将刚刚生成的canvas内容转成图片,并赋值给容器的 background-image 样式
const styleStr = `
width: ${elWidth}px;
height: ${elHeight}px;
position: absolute;
z-index: 9999999;
top: 0;
left: 0;
pointer-events: none;
background-image: url(${canvas.toDataURL('image/png')})
`
waterMaskDiv.setAttribute('style', styleStr)
el.appendChild(waterMaskDiv)
return styleStr
}
const vWatermark: Directive = {
mounted(el, { value }) {
// 将当前水印styleStr挂载到元素下,便于对比
el.waterMarkStyleStr = drawWatermark(el, value)
el.observer = new MutationObserver(() => {
const instance = document.getElementById(waterMarkId)
const style = instance?.getAttribute('style')
const { waterMarkStyleStr } = el
if (!instance) {
drawWatermark(el, value)
} else if (style !== waterMarkStyleStr) {
instance.setAttribute('style', waterMarkStyleStr)
}
})
el.observer.observe(document.body, {
childList: true,
attributes: true,
subtree: true,
})
},
unmounted(el) {
el.observer.disconnect()
el.observer = null
},
}
</script>
<style scoped>
.wrapper {
width: 600px;
height: 600px;
border: 2px solid rgb(202, 134, 8);
}
.wrapper-img {
height: 100%;
width: 100%;
}
</style>
我是老6
这样就能防住我吗?🤣🤣
姆猴移C,我反手就是一套禁用js再接Delete Element的组合拳,还是能去掉你的前端水印。 当然了,如果你直接让后端给图片或者文件加好水印后返回给前端渲染的话,emm...那我暂时是莫得办法😊。