Stories

Detail Return Return

Vue 項目一種簡單添加水印的方法 - Stories Detail

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);
}

代碼解析:手把手教學時間

  1. Canvas 繪製水印:我們創建了一個 canvas 元素,這就像在內存裏開了一個畫室。然後在其中繪製了隨機字符串作為水印內容,通過設置旋轉角度,讓水印呈斜角分佈,這是水印的"標準姿勢"。
  2. 動態調整大小:通過 measureText 方法獲取文字寬度,從而動態設置 canvas 的尺寸,確保水印效果最佳。別小看這一步,用户體驗可是大大提升。
  3. 轉為背景圖:使用 toDataURL 方法將 canvas 轉換為圖片 URL,作為水印元素的背景圖。這就像把畫好的畫拍個照,然後到處貼。
  4. 響應式高度:使用 ResizeObserver API 監聽 body 高度變化,確保水印能夠覆蓋整個頁面。畢竟誰也不想看到頁面拉長了水印卻還在半路。
  5. 防篡改機制:通過 MutationObserver API 監聽 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+

⚠️ 重要提醒:

  1. ResizeObserver 在 IE 中是不支持的,如果你的項目還需要兼容 IE,建議使用 resize-observer-polyfill 或者使用傳統的 window.resize 事件代替。
  2. MutationObserver 在 IE11+ 才得到支持,如果需要在更低版本的 IE 中使用,需要引入 polyfill。

優化建議:精益求精

  1. 水印內容個性化:示例中使用了隨機字符串,實際項目中可以替換為用户 ID、用户名等信息。這樣即使被截圖,也能追溯到具體用户。
  2. 性能優化:對於大型應用,可以考慮將水印元素設置為固定定位,並監聽滾動事件以優化性能。畢竟,我們不能因為一個水印影響了頁面性能。
  3. 更強的防篡改:除了使用 MutationObserver,還可以定期檢查水印元素是否存在,如果不存在則重新創建。甚至可以將水印信息存儲在多個地方,增加破解難度。
  4. Canvas 降級方案:對於不支持 Canvas 的古董瀏覽器,可以準備一張默認的水印圖片作為後備方案。

結語:

通過 Canvas 實現動態水印是一種靈活且功能強大的方案,可以根據業務需求進行個性化定製。在實際應用中,我們還可以結合其他技術手段來增強水印的安全性。

Add a new Comments

Some HTML is okay.