先上代码:
1、核心代码,实现自定义水印添加及监视元素阻止用户移除水印:
/**
* @param text 水印文字
* @param font 水印字体
* @param color 水印颜色
* @param deg 水印倾斜角度,负数上坡正数下坡
* @param gap 相邻两个水印的间距
*/
const config = {
attributes: true, // 属性的变动
attributeOldValue: true, // 布尔值,表示观察attributes变动时,是否需要记录变动前的属性值
attributeFilter: ['class', 'id', 'style'], // 数组,表示需要观察的特定属性(比如['class','src']
characterData: true, // 节点内容或节点文本的变动
childList: true, // 子节点的变动(指新增,删除或者更改)
subtree: true, // 布尔值,表示是否将该观察器应用于该节点的所有后代节点。
characterDataOldValue: true // 布尔值,表示观察characterData变动时,是否需要记录变动前的值。
}
function addWaterfall(el, {
text = '我是水印',
font = 'bold 34px STHeiti',
color = 'rgba(180,180,180,0.8)',
deg = -20,
gap = 200
}) {
// MutationObserver监听元素阻止用户操作dom
const ob = new MutationObserver(changeOb)
// 开始监听
function beginWatch() {
ob.observe(el, config)
}
// 停止监听,可通过observe重新监听
function stopWatch() {
ob.disconnect()
}
// 监听回调
function changeOb(mutationList) {
stopWatch()
console.log('修改了dom')
mutationList.forEach(mutation => {
switch (mutation.type) {
case 'attributes':
{
const attr = mutation.attributeName
if (attr === 'style') {
mutation.target.style = mutation.oldValue
} else if (attr === 'id') {
mutation.target.id = mutation.oldValue
}
}
}
})
beginWatch()
}
function createWaterfall() {
// 停止监听,否则会无限循环卡死
stopWatch()
// 创建canvas生成base64图片
const box_w = parseFloat(document.defaultView.getComputedStyle(el).width)
const box_h = parseFloat(document.defaultView.getComputedStyle(el).height)
const canvas = document.createElement('canvas')
canvas.id = 'canvas'
canvas.width = box_w
canvas.height = box_h
const ctx = canvas.getContext('2d')
ctx.rotate((deg * Math.PI) / 180)
ctx.font = font
ctx.fillStyle = color
// ctx.textAlign = 'left'
ctx.textBaseline = 'middle'
// 暴力渲染,在最多的行数或列数的基础上前后各多渲染二分之根号二的数量
const MAX_NUM = parseInt(Math.max(box_w, box_h) / gap) + 1
const LEFT = Math.floor(MAX_NUM / Math.sqrt(2) * -1)
const RIGHT = Math.ceil(MAX_NUM + MAX_NUM / Math.sqrt(2))
console.log(LEFT, RIGHT)
for (let i = LEFT; i <= RIGHT; i++) {
for (let j = LEFT; j <= RIGHT; j++) {
ctx.fillText(text, gap * i, gap * j)
}
}
el.style.background = `url(${canvas.toDataURL('image/png')})`
// 继续监听
beginWatch()
}
return createWaterfall
}
export default addWaterfall
2、全局自定义指令
// 全局自定义指令 waterfall
Vue.directive('waterfall', {
inserted(el, binding) {
let width = ''
let height = ''
const createWaterfall = addWaterfall(el, binding.value)
// 监听元素宽高发生改变,重新渲染水印
function resize() {
const style = document.defaultView.getComputedStyle(el)
if (width !== style.width || height !== style.height) {
console.log('resize')
createWaterfall()
}
width = style.width
height = style.height
}
resize()
el.__VueSetInterval = setInterval(resize, 300)
},
unbind(el) {
clearTimeout(el.__VueSetInterval)
}
})
3、自定义指令的使用
<div v-waterfall="{
text: '我是水印', // 各种水印相关的配置
}">
<span v-for="item in 50" :key="item" class="PdR50" style="line-height: 34px;"> 文本内容文本内容文本内容文本内容文本内容文本内容 </span>
</div>
4、效果展示,元素大小变化后会重新加水印,审查元素也无法去除水印
![](https://img-blog.csdnimg.cn/img_convert/f771c0617d287d22d9150e5c5d3fb97d.png)
相关技术的链接: