項(xiàng)目經(jīng)理老王:?? 緊急加需求! 現(xiàn)在水印不僅要全頁面覆蓋,還要遍布每個角落!用戶就算截個按鈕局部圖,也得帶著水印!代碼必須給全,從生成到防護(hù)一條龍!B端產(chǎn)品必須要全加水印,快快快...
碼農(nóng)小彬:?? 沒問題!上完整解決方案!
直接甩出完整代碼+原理分析??
?? 全頁面動態(tài)水印(Vue3 + Canvas + 防刪監(jiān)控)
? 核心目標(biāo)
- 全頁面密集水印 —— 無論用戶截取哪部分頁面,必帶水印
- 動態(tài)綁定用戶信息 —— 顯示
機(jī)密-{用戶名}-{時間}
- 防刪除/隱藏 —— 監(jiān)聽DOM變動自動恢復(fù)
- 零操作干擾 —— 透明+事件穿透
?? 完整代碼實(shí)現(xiàn)
1. 水印生成組件 Watermark.vue
這個代碼就是給整個網(wǎng)頁打上帶用戶信息和時間的透明水印,刪不掉還自動更新,防截圖防篡改。
<template>
<!-- 水印層(覆蓋整個視口) -->
<div ref="watermarkEl" class="global-watermark"></div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
const props = defineProps({
text: { type: String, default: '內(nèi)部保密' },
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('?? 檢測到水印被移除,已自動恢復(fù)!');
}
}
});
});
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. 在管理后臺入口調(diào)用
呃...這個代碼大概就是在網(wǎng)頁最外層加了個半透明的水印,寫著"機(jī)密數(shù)據(jù)",還綁定了當(dāng)前登錄用戶的ID,然后下面正常顯示網(wǎng)頁的其他內(nèi)容這樣子!
<template>
<div id="app">
<Watermark
text="機(jī)密數(shù)據(jù)"
: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>
??? 增強(qiáng)防護(hù)
1. 禁用開發(fā)者工具(可選)
這個代碼就是...如果有人想按F12或者Ctrl+Shift+I打開瀏覽器開發(fā)者工具,網(wǎng)頁就會彈窗警告。
document.addEventListener('keydown', (e) => {
if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && e.key === 'I')) {
e.preventDefault();
alert('禁止開發(fā)者工具!');
}
});
2. 動態(tài)水印刷新(防截圖拼接)
這個代碼就是...讓水印每隔1小時變一次!
setInterval(() => {
updateWatermark();
}, 60 * 60 * 1000);
?? 關(guān)鍵點(diǎn)說明
特性 | 實(shí)現(xiàn)方式 | 效果 |
---|
全頁面覆蓋 | background-repeat: repeat | 無論頁面多大,水印無限平鋪 |
動態(tài)內(nèi)容 | 綁定userId +時間戳 | 每個用戶水印唯一,可追溯 |
防刪除 | MutationObserver 監(jiān)聽DOM | 刪除后自動重新插入 |
操作無阻 | pointer-events: none | 可點(diǎn)擊下方按鈕/輸入框 |
?? 注意事項(xiàng)
-
- 性能優(yōu)化:水印密度(
density
)建議≥100px,避免Canvas渲染壓力 -
- 移動端適配:測試
100vh
在移動端的表現(xiàn),必要時改用window.innerHeight
-
- 有時候可能還需要后端做一些操作,前端水印顯示用戶ID和時間,后端同時記錄操作日志,一旦泄露就能通過水印信息查后端日志精準(zhǔn)定位責(zé)任人(就像快遞面單+物流系統(tǒng),撕掉面單也能通過系統(tǒng)查誰寄的)。