Vue 項目中動態添加水印的實現方式
實現思路:Canvas 大法好
水印這個東西,説白了就是一些半透明的文字或者圖案,覆蓋在頁面內容之上。我們這次請出 Canvas 這位重量級嘉賓。Canvas 就像一個畫布,我們可以在上面隨意塗鴉,然後把它當作背景圖貼到頁面上。
核心代碼實現:
setWaterMark() {
// 創建一個看不見的"畫布"
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 生成一個隨機字符串,假裝這是什麼高深的加密信息
const text = Math.random().toString();
// 測量一下文字寬度,別畫出邊界了
const textWidth = ctx.measureText(text).width;
canvas.width = textWidth + 50; // 留點邊距,別太擠
canvas.height = textWidth + 50;
// 重新設置字體,因為設置寬高會重置上下文,這很"智能"...
ctx.font = '20px Arial';
ctx.fillStyle = '#e3e3e3'; // 高雅黑色,低調奢華有內涵
ctx.translate(canvas.width/2, canvas.width/2); // 移動到中心,別畫偏了
ctx.rotate(-45 * Math.PI / 180); // 旋轉45度,斜着才好看
ctx.fillText(text, -textWidth/2, 10); // 開始"塗鴉"
// 把畫布變成圖片,這波操作6不6?
const dataURL = canvas.toDataURL('image/png');
// 創建水印元素,名字起得很隨意
let _elem = document.createElement('water-mark');
_elem.id = 'water-mark';
_elem.style.width = '100%';
_elem.style.height = document.body.scrollHeight + 'px';
_elem.style.position = 'absolute';
_elem.style.zIndex = '9999999999'; // 層級高到離譜,生怕顯示不出來
_elem.style.pointerEvents = 'none'; // 點擊穿透,用户操作不到它,就像幽靈一樣
_elem.style.opacity = '0.4'; // 半透明,不要太搶眼
_elem.style.backgroundImage = `url(${dataURL})`;
_elem.style.backgroundRepeat = 'repeat';
document.body.insertBefore(_elem, document.body.firstChild);
// 添加防篡改機制,防止用户通過開發者工具刪除水印
const mutationObserver = new MutationObserver((mutationsList) => {
// 遍歷所有發生變化的地方
mutationsList.forEach((mutation) => {
// 檢查是否有節點被刪除
mutation.removedNodes.forEach((node) => {
// 如果被刪除的節點是我們的水印元素
if (node === _elem) {
// 重新把水印加回到頁面上
document.body.appendChild(_elem);
}
});
// 檢查是否有屬性被修改(比如把水印設為隱藏)
if (mutation.type === 'attributes' && mutation.target === _elem) {
// 確保水印保持可見狀態
_elem.style.display = 'block';
_elem.style.opacity = '0.4';
// 還可以添加更多屬性檢查...
}
});
});
// 開始監控頁面變化,包括子元素變化、屬性變化等
mutationObserver.observe(document.body, {
childList: true, // 監控直接子節點的變化
subtree: true, // 監控所有後代節點的變化
attributes: true // 監控屬性的變化
});
// 監聽頁面高度變化,畢竟內容可能會變高
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
if (entry.target === document.body) {
_elem.style.height = document.body.scrollHeight + 'px';
}
}
});
resizeObserver.observe(document.body);
}
代碼解析:手把手教學時間
- Canvas 繪製水印:我們創建了一個 canvas 元素,這就像在內存裏開了一個畫室。然後在其中繪製了隨機字符串作為水印內容,通過設置旋轉角度,讓水印呈斜角分佈,這是水印的"標準姿勢"。
- 動態調整大小:通過
measureText方法獲取文字寬度,從而動態設置 canvas 的尺寸,確保水印效果最佳。別小看這一步,用户體驗可是大大提升。 - 轉為背景圖:使用
toDataURL方法將 canvas 轉換為圖片 URL,作為水印元素的背景圖。這就像把畫好的畫拍個照,然後到處貼。 - 響應式高度:使用
ResizeObserverAPI 監聽 body 高度變化,確保水印能夠覆蓋整個頁面。畢竟誰也不想看到頁面拉長了水印卻還在半路。 - 防篡改機制:通過
MutationObserverAPI 監聽 DOM 變化,就像一個盡職的保安,時刻盯着水印元素。一旦發現有人試圖刪除或隱藏水印,它就會立即恢復水印,讓"破壞者"的計劃落空。
瀏覽器兼容性:
| 特性 | Chrome | Firefox | Safari | Edge | IE |
|---|---|---|---|---|---|
| Canvas 基礎支持 | 4+ | 3.6+ | 4+ | 9+ | 9+ |
| Canvas getContext | 4+ | 3.6+ | 4+ | 9+ | 9+ |
| toDataURL | 4+ | 3.6+ | 4+ | 9+ | 9+ |
| ResizeObserver | 64+ | 69+ | 13.1+ | 79+ | ❌ |
| MutationObserver | 26+ | 14+ | 7+ | 12+ | 11+ |
⚠️ 重要提醒:
ResizeObserver在 IE 中是不支持的,如果你的項目還需要兼容 IE,建議使用 resize-observer-polyfill 或者使用傳統的window.resize事件代替。MutationObserver在 IE11+ 才得到支持,如果需要在更低版本的 IE 中使用,需要引入 polyfill。
優化建議:精益求精
- 水印內容個性化:示例中使用了隨機字符串,實際項目中可以替換為用户 ID、用户名等信息。這樣即使被截圖,也能追溯到具體用户。
- 性能優化:對於大型應用,可以考慮將水印元素設置為固定定位,並監聽滾動事件以優化性能。畢竟,我們不能因為一個水印影響了頁面性能。
- 更強的防篡改:除了使用 MutationObserver,還可以定期檢查水印元素是否存在,如果不存在則重新創建。甚至可以將水印信息存儲在多個地方,增加破解難度。
- Canvas 降級方案:對於不支持 Canvas 的古董瀏覽器,可以準備一張默認的水印圖片作為後備方案。
結語:
通過 Canvas 實現動態水印是一種靈活且功能強大的方案,可以根據業務需求進行個性化定製。在實際應用中,我們還可以結合其他技術手段來增強水印的安全性。