项目经理老王:🔥 紧急加需求! 现在水印不仅要全页面覆盖,还要遍布每个角落!用户就算截个按钮局部图,也得带着水印!代码必须给全,从生成到防护一条龙!B端产品必须要全加水印,快快快...
码农小彬:💪 没问题!上完整解决方案!
直接甩出完整代码+原理分析👇
📌 全页面动态水印(Vue3 + Canvas + 防删监控)
✨ 核心目标
- 全页面密集水印 —— 无论用户截取哪部分页面,必带水印
- 动态绑定用户信息 —— 显示
机密-{用户名}-{时间}
- 防删除/隐藏 —— 监听DOM变动自动恢复
- 零操作干扰 —— 透明+事件穿透
🚀 完整代码实现
1. 水印生成组件 Watermark.vue
这个代码就是给整个网页打上带用户信息和时间的透明水印,删不掉还自动更新,防截图防篡改。
<template>
<!-- 水印层(覆盖整个视口) -->
<div ref="watermarkEl" class="global-watermark"></div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
const props = defineProps({
text: { type: String, default: '内部保密' },
userId: { type: String },
opacity: { type: Number, default: 0.1 },
density: { type: Number, default: 150 },
});
const watermarkEl = ref(null);
const generateWatermark = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const size = props.density;
canvas.width = size * 2;
canvas.height = size * 2;
ctx.font = '14px Arial';
ctx.fillStyle = `rgba(100, 100, 100, ${props.opacity})`;
ctx.rotate(-25 * Math.PI / 180);
const dynamicText = `${props.text} - ${props.userId || '未知用户'} - ${new Date().toLocaleString()}`;
ctx.fillText(dynamicText, 10, size);
return canvas.toDataURL('image/png');
};
const updateWatermark = () => {
if (!watermarkEl.value) return;
watermarkEl.value.style.backgroundImage = `url(${generateWatermark()})`;
};
watch([() => props.text, () => props.userId], updateWatermark);
const initObserver = () => {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.removedNodes.length) {
const removed = Array.from(mutation.removedNodes);
if (removed.some(node => node === watermarkEl.value)) {
document.body.appendChild(watermarkEl.value);
console.warn('⚠️ 检测到水印被移除,已自动恢复!');
}
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
};
onMounted(() => {
updateWatermark();
initObserver();
});
</script>
<style scoped>
.global-watermark {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-repeat: repeat;
pointer-events: none;
z-index: 9999;
opacity: v-bind('props.opacity');
}
</style>
2. 在管理后台入口调用
呃...这个代码大概就是在网页最外层加了个半透明的水印,写着"机密数据",还绑定了当前登录用户的ID,然后下面正常显示网页的其他内容这样子!
<template>
<div id="app">
<Watermark
text="机密数据"
:userId="currentUser.id"
:opacity="0.15"
:density="120"
/>
<router-view />
</div>
</template>
<script setup>
import Watermark from '@/components/Watermark.vue';
import { useAuthStore } from '@/stores/auth';
const currentUser = useAuthStore().user;
</script>
🛡️ 增强防护
1. 禁用开发者工具(可选)
这个代码就是...如果有人想按F12或者Ctrl+Shift+I打开浏览器开发者工具,网页就会弹窗警告。
document.addEventListener('keydown', (e) => {
if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && e.key === 'I')) {
e.preventDefault();
alert('禁止开发者工具!');
}
});
2. 动态水印刷新(防截图拼接)
这个代码就是...让水印每隔1小时变一次!
setInterval(() => {
updateWatermark();
}, 60 * 60 * 1000);
📝 关键点说明
特性 | 实现方式 | 效果 |
---|
全页面覆盖 | background-repeat: repeat | 无论页面多大,水印无限平铺 |
动态内容 | 绑定userId +时间戳 | 每个用户水印唯一,可追溯 |
防删除 | MutationObserver 监听DOM | 删除后自动重新插入 |
操作无阻 | pointer-events: none | 可点击下方按钮/输入框 |
🚨 注意事项
-
- 性能优化:水印密度(
density
)建议≥100px,避免Canvas渲染压力 -
- 移动端适配:测试
100vh
在移动端的表现,必要时改用window.innerHeight
-
- 有时候可能还需要后端做一些操作,前端水印显示用户ID和时间,后端同时记录操作日志,一旦泄露就能通过水印信息查后端日志精准定位责任人(就像快递面单+物流系统,撕掉面单也能通过系统查谁寄的)。