- 创建水印容器,canvas绘制水印内容图,将图片设置为水印容器的背景图平铺。水印容器添加到需要加水印的DOM之内,position定位形式铺满目标DOM
// 需要添加水印的DOM
const boxRef = ref()
// 水印
const watermarkEl = ref(null)
// 水印背景图配置项
const defaultConfig = {
/** 文本颜色 */
color: "#c0c4cc",
/** 文本透明度 */
opacity: 0.5,
/** 文本字体大小 */
size: 16,
/** 文本字体 */
family: "serif",
/** 文本倾斜角度 */
angle: -20,
/** 一处水印所占宽度(数值越大水印密度越低) */
width: 300,
/** 一处水印所占高度(数值越大水印密度越低) */
height: 200,
/** 水印文本*/
backupText:"水印文本"
}
// canvas绘制返回水印背景图
const createBase64 = () => {
// 解构配置
const {color,opacity,size,family,angle,width,height,backupText} = defaultConfig
// 创建一个画布
const canvasEl = document.createElement("canvas")
canvasEl.width = width
canvasEl.height = height
//创建 context 对象,getContext("2d") 对象是内建的 HTML5 对象,
//拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法
const ctx = canvasEl.getContext("2d")
if (ctx) {
ctx.fill = color
ctx.globalAlpha = opacity
ctx.font = `${size}px ${family}`
ctx.rotate((Math.PI/180)*angle)
ctx.fillText(backupText,0,height/2)
}
return canvasEl.toDataURL()
}
// 创建添加水印
const createWatermark = () => {
// parentEl需要添加水印的dom
let parentEl = boxRef.value
if (!watermarkEl.value) {
// markEl水印容器
const markEl = document.createElement("div")
// markEl样式设置
markEl.setAttribute('style', `background: url(${createBase64()}) left top repeat; position: absolute; top: 0; left: 0; bottom: 0; right: 0; pointer-events: none; z-index: 0;`)
// ref留存markEl用于后续更新,避免多次绘制
watermarkEl.value = markEl
}
// 将水印添加到目标dom内
parentEl.appendChild(watermarkEl.value)
// 监听水印元素和容器元素,下面步骤需要
addMutationListener()
}
onMounted(() => {
createWatermark()
})
实现效果:
- 步骤1其实已经完成了水印的添加,如果没过多追求就已经实现了简单的水印添加功能。但因为水印也是一个真实存在的DOM,可以通过打开控制台修改水印DOM节点样式属性或删除水印DOM来实现去水印的目的。
解决思路:监听用户修改水印和父级DOM的属性修改、DOM删除等用户行为,重新添加设置水印。JS提供了MutationObserver的相关API,MutationObserver提供了监测针对某一范围内DOM发生变化的即时反应处理能力。具体实现如下
// reactive装载监听器
const observer = reactive({
watermarkElMutationObserver: undefined,
parentElMutationObserver: undefined
})
//移除 mutation 监听
const removeListener = (kind = "all")=>{
if(kind === "mutation"||kind ==="all"){
observer.parentElMutationObserver?.disconnect();
observer.parentElMutationObserver = undefined;
observer.watermarkElMutationObserver?.disconnect();
observer.watermarkElMutationObserver = undefined;
}
}
// 清除水印
const clearWatermark = () => {
if (!boxRef.value || !watermarkEl.value) return
removeListener()
try{
//移除水印元素
boxRef.value.removeChild(watermarkEl.value)
}catch{
//比如在无防御情况下,用户打开控制台删除了这个元素
console.warn("水印元素已不存在,请重新创建")
}finally{
watermarkEl.value = null
}
}
// 更新水印
const updateWatermark = debounce(()=>{
clearWatermark()
createWatermark()
},100)
// MutationObserver监听回调
const mutationCallback = (mutationList)=> {
//水印的防御 (防止用户手动删除水印或通过css隐藏水印)
console.log('--回调,发生改变--',mutationList)
mutationList.forEach(mutation => {
switch (mutation.type) {
// dom样式属性发送变化,重置更新水印
case "attributes":
mutation.target === watermarkEl.value && updateWatermark()
break;
// 子节点发送变化,且存在removeNodes(删除节点行为),重新添加上水印
case "childList":
mutation.removedNodes.forEach((item)=>{
item === watermarkEl.value && boxRef.value.appendChild(watermarkEl.value);
})
break
}
})
}
// 添加监听
const addMutationListener = () => {
let parentEl = boxRef.value
if(!observer.parentElMutationObserver) observer.parentElMutationObserver = new MutationObserver(mutationCallback)
if(!observer.watermarkElMutationObserver) observer.watermarkElMutationObserver = new MutationObserver(mutationCallback)
observer.parentElMutationObserver.observe(parentEl, {
// 观察目标节点属性是否变动,默认为 true
attributes: true,
// 观察目标子节点是否有添加或者删除,默认为 false
childList: true,
// 是否拓展到观察所有后代节点,默认为 false
subtree: false
})
observer.watermarkElMutationObserver.observe(watermarkEl.value, {
// 观察目标节点属性是否变动,默认为 true
attributes: true,
// 观察目标子节点是否有添加或者删除,默认为 false
childList: false,
// 是否拓展到观察所有后代节点,默认为 false
subtree: false
})
}
上面步骤简要概述就是在添加MutationObserver后,通过回调函数判断用户行为。删除或修改行为直接将watermarkEl重新添加、更新到水印的父节点,达到保持水印节点存在及样式不被修改。
注意项:在初步实现过程中,对水印的节点DOM想通过class设置样式属性,而未通过js的setAttribute属性设置语句来进行水印样式设置。这会产生一个问题,在控制台中可通过对应class类名样式修改而不会触发MutationObserver的监听回调事件。所以不能给水印节点添加class类名形式实现样式。
不足之处:以上实现水印功能后并非无懈可击,用户可通过页面加载完成后禁用js达到屏蔽水印层的操作。
如何禁用JavaScript?
- f12打开浏览器控制台
- 按 Ctrl+Shift+P (Windows、Linux) 或 Command+Shift+P (macOS) 打开 命令菜单
- 开始键入
javascript,选择“ 禁用 JavaScript [调试器]”,然后按 Enter
- 选项卡中的Sources出现黄色警告标识标识已经禁用成功
解决此缺陷只能禁用浏览器调试???