关于前端:前端水印如此简单

38次阅读

共计 3919 个字符,预计需要花费 10 分钟才能阅读完成。

咱们从实现形式上来思考,既然要做水印,那必定要是全屏幕的,咱们会先想到几点

  1. 用一个 div 全屏 fixed。
  2. 水印要和登录信息绑定,那么咱们从 cookie 中获取一下账号信息。
  3. 屏幕上一个大的水印,成果没有稀稀拉拉的小水印成果好。
  4. 水印之前的间距要小一点,这样能力减少覆盖面积。
  5. 要有避免篡改的性能。

列出以上这几点,咱们就顺次实现就好了。

第一点

很好实现。咱们 create 一个 dom 元素,插入到 body 中就能够了。

 const divObj = document.createElement('div');
    const styleStr = `
                  position:fixed;
                  top:0;
                  left:0;
                  bottom:0;
                  right:0;
                  z-index:999999;
                  background-repeat:repeat;
                  `;
    divObj.setAttribute('style', styleStr);
    document.body.appendChild(divObj);

第二点

水印的内容咱们从 cookie 中获取一下。

 const user = /user_name=([^;]+)/.exec(document.cookie);
 const name = Array.isArray(user) && user.length === 2 && user[1] ? user[1] : '配置的水印';

这里的 name 就是咱们拿到的用户信息。

第三四点

这两个综合思考的话,将 name 作为一个背景图 而后 repeat 就能够了。咱们要将文字转为图片,那么 canvas 是一个不错的抉择。同时咱们要思考到这个图片不易过大(为了合乎第三点),所以咱们就依照 200*100 的尺寸吧。

    const canvasObj = document.createElement('canvas');
    const canvas2d = canvasObj.getContext('2d');
    canvasObj.width = 200;
    canvasObj.height = 100;
    canvas2d.font = fontSize + 'px Arial';
    canvas2d.fillStyle = 'rgba(128,128,128,.6)'; // 这里文字的色彩淡一点,不要影响整体的好看
    canvas2d.translate(canvasObj.width / 4, canvasObj.height / 2);
    canvas2d.rotate((-30 / 180) * Math.PI);
    canvas2d.fillText(name, 0, canvasObj.height / 2);
    // 将 canvas 转为 dataURL
    const base64Url = canvasObj.toDataURL('image/png');

当初咱们就拿到了一个 base64 的地址,能够用来作为咱们的图片。

第五点

要有防篡改的性能,具体要体现在咱们创立的水印不能轻易的让他人给删了,其次,水印内容也不能轻易的被改了,无论是水印内容或者是水印的色彩款式等。

水印内容这一块因为咱们应用的是 cookie 中的登录信息,如果有人更改了 cookie 中的值,会导致登录信息生效,在 sso 这一侧就会被强制跳转到登录页面,所以这个能够交由登录零碎来做。

咱们的 dom 不能轻易的被删除和更改款式,那么咱们就须要用到 MutationObserver 这个 api 了,他的作用就是监听 DOM 的变动,并触发一个回调,咱们只需在回调中从新执行水印的办法就能够防止这个问题。

   if (MutationObserver) {let waterMarkOb = new MutationObserver(function () {const _globalWatermark = document.querySelector(`domId`);
        // 当款式或者水印元素 dom 节点有改变时会从新绘制
        if ((_globalWatermark && _globalWatermark.getAttribute('style') !== styleStr) ||
          !_globalWatermark
        ) {waterMarkOb.disconnect();
          waterMarkOb = null;
          setWaterMark();}
      });
      // 指定察看对象
      waterMarkOb.observe(document.body, {
        attributes: true,
        subtree: true,
        childList: true,
      });
    }

最初一点

注意事项中有一点,就是咱们的水印性能尽可能的不要影响到业务的应用。
所以这里咱们要思考两点:

  1. 咱们的水印 dom 在全屏幕的最上层,尽管层级在 99999,然而也不要要影响到上面的元素操作。所以咱们须要减少 pointer-events:none 的属性,不影响鼠标的操作。
  2. 咱们要在前端代码不变动的状况下上线水印的性能,那咱们就要从服务器上动手了,比方在 nginx 中,咱们能够应用 sub_filter 模块来替换返回的文本。例如

    subs_filter "(<\/body>)" "$1<script src=\"https://cdn.xxx.com/watermark.js\"></script>" irg;

    到此咱们的水印性能就算实现了。
    一贯的准则,「BB is nothing,show me the code」。
    整体的代码如下:

!(function () {
  // 一个配置
  const options = {
    id: 'globalWaterMark',
    fontSize: 10,
    color: 'rgba(128,128,128,.6)',
    rotate: '-30',
    userName: "其余的身份"
  };

  /**
   * 创立水印图片 url
   */
  function createWaterMark() {const { fontSize, color, id} = options;
    const user = /user_name=([^;]+)/.exec(document.cookie);
    const name = Array.isArray(user) && user.length === 2 && user[1] ? user[1] : options.userName;
    const canvasObj = document.createElement('canvas');
    const canvas2d = canvasObj.getContext('2d');
    canvasObj.width = 200;
    canvasObj.height = 100;
    canvas2d.font = fontSize + 'px Arial';
    canvas2d.fillStyle = color;
    canvas2d.translate(canvasObj.width / 4, canvasObj.height / 2);
    canvas2d.rotate((-30 / 180) * Math.PI);
    canvas2d.fillText(name, 0, canvasObj.height / 2);
    // 将 canvas 转为 dataURL
    const base64Url = canvasObj.toDataURL('image/png');
    return base64Url;
  }

  function setWaterMark() {const { fontSize, color, id} = options;
    const url = createWaterMark();
    const target = document.getElementById(id);
    if(target){document.body.removeChild(target)
    }
    const divObj = document.createElement('div');
    divObj.id = options.id;
    const styleStr = `
                  position:fixed;
                  top:0;
                  left:0;
                  bottom:0;
                  right:0;
                  z-index:999999;
                  background-repeat:repeat;
                  pointer-events:none;
                  background-image:url('${url}')`;
    divObj.setAttribute('style', styleStr);
    document.body.appendChild(divObj);
    // 监听 DOM 变动
    const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    if (MutationObserver) {let waterMarkOb = new MutationObserver(function () {const _globalWatermark = document.querySelector(`#${id}`);
        // 当款式或者水印元素 dom 节点有改变时会从新绘制
        if ((_globalWatermark && _globalWatermark.getAttribute('style') !== styleStr) ||
          !_globalWatermark
        ) {waterMarkOb.disconnect();
          waterMarkOb = null;
          setWaterMark();}
      });
      // 指定察看对象
      waterMarkOb.observe(document.body, {
        attributes: true,
        subtree: true,
        childList: true,
      });
    }
  }

  document.addEventListener('DOMContentLoaded', function () {setWaterMark();
  });
})();

正文完
 0