如何實現一個React水印組件

基本介紹 📝

  • 前陣子經過 前端水印生成方案(網頁水印+圖片水印) 這篇文章學習了一下水印的生成方案,對其中使用 Canvas 實現網頁水印的方案十分感興趣,因而對相關代碼加以修改,實現了一個 React 水印組件併發布到 👉 npm
  • 該組件是經過 Canvas 生成水印,並使用 MutationObserve (能夠監聽DOM結構變化的接口)監視 DOM 的變更,使得水印不可被刪除、且屬性不可被修改。
  • 關於發佈react組件的方法以前已在使用npm發佈一個react組件(踩坑實踐)中進行介紹,本文將再也不進行闡述,本文着重介紹組件的使用效果及具體實現。

效果演示 🤩

  • 該組件已發佈到npm上,可以使用如下命令安裝:
npm install --save watermark-component-for-react
複製代碼
  • 在項目中使用:
import React from 'react';
import WaterMark from 'watermark-component-for-react';
import * as styles from './index.css';

class ReactDemo extends React.Component{
  render () {
    const content = `內部文檔,請勿外傳 by-前端小黑`;
    return (
      <WaterMark content={content}>
        <div className={styles.wrapper}>hello world</div>
      </WaterMark>
    );
  }
}
export default ReactDemo;
複製代碼
  • 結果:
  • ⚠️注意:須要加水印的組件須要包裹在 WaterMark 組件中,即做爲 WaterMark 的子組件。

組件特色

  1. 使用 MutationObserve 監視 DOM 的變更,水印不可被刪除、且屬性不可被修改: css

  2. 豐富的 props 使得水印組件可實現定製化需求:前端

成員 說明 類型 默認值
style watermark最外層組件內聯樣式 undefined | object undefined
container 容器 HTMLDivElement document.body
width canvas元素寬 string 300
height canvas元素高 string 200
textAlign 繪製文本的對齊方式 string left
textBaseline 文本基準線 string bottom
font 字體大小及樣式 string 16px Microsoft Yahei
fillStyle 自定義水印的顏色 string black
content 水印內容 string 內部文檔,請勿外傳
globalAlpha 設置圖形和圖像透明度的值 number 0.1
rotate 文字旋轉角度 number 16
zIndex 元素堆疊順序 number 1000

具體實現 🧐

  • 水印組件的源碼以下:
import * as React from 'react';
import { watermark } from '../../utils/lib'

export default class WaterMark extends React.Component {
  constructor(props) {
    super(props);

    this.container = null;
  }

  componentDidMount () {
    const { style, ...options } = this.props;
    watermark({
      container: this.container,
      ...options,
    });
  }

  render () {
    const style = {
      position: 'relative',
      ...this.props.style,
    };
    return (
      <div ref={(el) => this.container = el} id="watermark" style={style}>
        {this.props.children}
      </div>
    );
  }
}
複製代碼
  • 核心代碼是 watermark 這個方法,具體代碼以下:
export function watermark (options) {
  const {
    container = document.body, // 容器
    width = '300', // canvas元素寬
    height = '200', // canvas元素高
    textAlign = 'left', // 文字對齊
    textBaseline = 'bottom', // 基準線
    font = '16px Microsoft Yahei', // 字體大小及樣式
    fillStyle = '#000', // 自定義水印的顏色
    content = '內部文檔,請勿外傳', // 水印內容
    globalAlpha = 0.1, // 設置圖形和圖像透明度的值
    rotate = 16, // 文字旋轉角度
    zIndex = 1000, // 元素堆疊順序
  } = options;

  let canvas = document.createElement('canvas');
  canvas.setAttribute('width', width);
  canvas.setAttribute('height', height);
  let ctx = canvas.getContext('2d'); // 獲取 canvas2d 上下文
  ctx.globalAlpha = globalAlpha;
  ctx.textAlign = textAlign;
  ctx.textBaseline = textBaseline;
  ctx.font = font;
  ctx.fillStyle = fillStyle;
  ctx.rotate((Math.PI * rotate) / 180);
  ctx.fillText(content, 50, 50);

  const base64Url = canvas.toDataURL(); // 返回一個包含圖片展現的 data URI

  const __wm = document.querySelector('.__wm');//選擇器
  const watermarkDiv = __wm || document.createElement("div");
  const styleStr = `
    position:absolute;
    top:0px;
    left:0px;
    width:100%;
    height:100%;
    z-index:${zIndex};
    pointer-events:none;
    background-repeat:repeat;
    background-image:url('${base64Url}')`;

  watermarkDiv.setAttribute('style', styleStr);
  watermarkDiv.classList.add('__wm'); // 爲元素添加「__wm」類名

  container.style.position = 'relative';
  if (!__wm) {
    container.appendChild(watermarkDiv); // 添加元素
  }

  const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
  // 檢查瀏覽器是否支持這個API
  if (MutationObserver) {
    const args = arguments[0];
    let mo = new MutationObserver(function () {
      const __wm = document.querySelector('.__wm');
      // 只在__wm元素變更才從新調用
      if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm || container.style.position !== 'relative') {
        // 避免一直觸發
        mo.disconnect();
        mo = null;
        watermark(args);
      }
    });
    mo.observe(container, {
      attributes: true, // 觀察目標節點的屬性節點
      subtree: true, // 觀察目標節點的全部後代節點
      childList: true, // 觀察目標節點的子節點
    })
  }
};
複製代碼
  • 爲便於理解,已在以上文件加上了註釋,能夠看到,經過使用MutationObserve ,當水印組件被刪除、屬性被修改或水印組件的容器定位屬性 position 不爲 relative時,會從新調用watermark方法。

總結 👀

  • 爲了不公司的內部文檔被截圖外泄,有必要在系統頁面上面增長水印。
  • 爲了不某些用戶將水印刪除,須要對水印組件進行監聽(經過 MutationObserve 實現),當組件被刪除時動態插入新的水印。
  • 本文介紹的水印組件(watermark-component-for-react)已發佈到 NPM,歡迎你們下載使用。

以上內容若有遺漏錯誤,歡迎留言 ✍️指出,一塊兒進步💪💪💪react

若是以爲本文對你有幫助,🏀🏀留下你寶貴的 👍npm

參考資料

  1. 前端水印生成方案(網頁水印+圖片水印)
  2. Canvas API MDN文檔
  3. MutationObserver MDN文檔
  4. 使用npm發佈一個react組件(踩坑實踐)
相關文章
相關標籤/搜索