4
头图

We consider the implementation method. Since we want to make a watermark, it must be a full screen. We will think of a few points first.

  1. Fixed with a div fullscreen.
  2. The watermark needs to be bound to the login information, then we get the account information from the cookie.
  3. A large watermark on the screen, the effect is not as good as the dense small watermark.
  4. The spacing before the watermark should be smaller, so as to increase the coverage area.
  5. There must be a function of preventing tampering.

List the above points, and we will implement them in turn.

The first point

Well done. We create a dom element and insert it into the 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);

Second point

The content of the watermark is obtained from the cookie.

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

The name here is the user information we got.

The third and fourth points

If these two are considered comprehensively, just use the name as a background image and repeat it. We want to convert text into pictures, then canvas is a good choice. At the same time, we have to consider that this picture is not easy to be too large (in order to meet the third point), so let's follow the size of 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');

Now we have a base64 address that can be used as our image.

Fifth

To have the function of preventing tampering, it should be reflected in the fact that the watermark we created cannot be easily deleted by others, and secondly, the content of the watermark cannot be easily changed, whether it is the content of the watermark or the color style of the watermark.

The watermark content is because we use the login information in the cookie. If someone changes the value in the cookie, the login information will be invalid, and the sso side will be forced to jump to the login page, so this can be handed over to Log in to the system to do it.

Our dom cannot be easily deleted and styled, so we need to use the MutationObserver api. Its function is to monitor changes in the DOM and trigger a callback. We only need to re-execute the watermark method in the callback. Avoid this problem.

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

the last point

One thing to note is that our watermark function should not affect the use of business as much as possible.
So here we have to consider two things:

  1. Our watermark dom is at the top of the full screen. Although the level is 99999, it should not affect the operation of the elements below. So we need to increase the properties of pointer-events:none without affecting the operation of the mouse.
  2. We want to launch the watermark function without changing the front-end code, then we have to start from the server. For example, in nginx, we can use the sub_filter module to replace the returned text. E.g

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

    At this point, our watermark function is realized.
    The consistent principle, "BB is nothing, show me the code".
    The overall code is as follows:

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


Manster
17 声望1 粉丝