前端添加水印

157 阅读2分钟
常见的水印就是微博、抖音平台,转载内容时,会加上自己的名片信息,增加知名度。

这两天有时间看了下浏览器加水印的一些实现,总结下  
水印是在浏览器元素的最上层级加上一个水印层 top-layer,
水印层是按照一定规则显示文本或图片

代码

// 废话少说上代码, 支持文本,图片,防篡改功能
// watermark.js
export const watermark = {
  uuid: 0,
  dom: null,
  waterMarkDom: null,
  defaultOpts: {
    width: 200,
    height: 200,
    offsetLeft: 0,
    offsetTop: 0,
    angle: 30,
    opacity: 0.5,
    zIndex: 99999,
    color: "#000000",
    font: "18px Microsoft YaHei",
    mark: "",
  },
  opts: {},
  init: async function (opts) {
    this.opts = { ...this.defaultOpts, ...opts };
    
    this.opts.bgUrl = await this.generateText2WaterPic();
    this.getEl();
    this.addWaterMark();
    this.observerMark();
  },
  generateImg2WaterPic: function () {},
  // 生成水印图
  generateText2WaterPic: function () {
    const { height, width, angle, offsetLeft, offsetTop, color, font, mark, imgUrl = "", imgAttrs = [0, 0] } = this.opts;

    const can = document.createElement("canvas");
    can.width = width;
    can.height = height;

    const ctx = can.getContext("2d");

    if (imgUrl) {
      return new Promise((resolve, inject) => {
        var img = new Image();
        img.src = imgUrl;
        img.onload = function (e) {
          ctx.drawImage(img, ...imgAttrs);
          // ctx.drawImage(img, sx, sy, swidth, sheight, x, y, width, height);
          const src = can.toDataURL("image/png");
          resolve(src);
        };
      });
    } else {
      //  高度默认是sin + 文字高度, 这样文字才完全显示出来
      let h = Math.sin(angle / 360) * width + 18;

      ctx.rotate(-angle / 360);
      ctx.font = font;
      ctx.fillStyle = color;
      ctx.textAlign = "left";
      // 设置文字开始位置
      ctx.fillText(mark, 0 + offsetLeft, h + offsetTop);
      return can.toDataURL("image/png");
    }
  },
  // 生成加水印的dom元素
  addWaterMark: function () {
    const { el, opacity, zIndex, bgUrl, left = 0, right = 0, top = 0, bottom = 0, bgSize = "auto" } = this.opts;
    const div = document.createElement("div");
    this.uuid++;
    div.id = `water-mark-${this.uuid}`;
    let styles = {
      "pointer-events": "none",
      opacity: opacity,
      "z-index": zIndex,
      left: left + "px",
      right: right + "px",
      top: top + "px",
      bottom: bottom + "px",
      background: `url(${bgUrl}) left top repeat`,
      "background-size": bgSize,
    };

    if (!el) {
      styles.position = "fixed";
    } else {
      this.dom.style.position = "relative";
      styles.position = "absolute";
    }
    let str = "";
    Object.keys(styles).forEach((k) => {
      str += `${k}: ${styles[k]};`;
    });
    this.opts.style = str;
    div.style = str;
    this.waterMarkDom = div;
    this.dom.appendChild(div);
  },
  // 获取添加水印的dom元素,如果没有写,默认加到body上
  getEl: function () {
    const { el } = this.opts || this.defaultOpts;
    let dom;
    if (!el) {
      dom = document.body;
    } else {
      dom = document.getElementById(el);
    }
    if (!dom) {
      dom = document.body;
    }
    this.dom = dom;
  },
  // 监测水印防止删除,或改动
  observerMark() {
    const { style } = this.opts;
    const body = document.body;
    const waterMarkDom = this.waterMarkDom;
    const _this = this;
    const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    if (MutationObserver) {
      let mo = new MutationObserver(function () {
        //
        if ((waterMarkDom && waterMarkDom.getAttribute("style") !== style) || !waterMarkDom) {
          // 避免一直触发
          mo.disconnect();
          mo = null;
          _this.addWaterMark();
          _this.observerMark();
        }
      });

      mo.observe(body, {
        attributes: true,
        subtree: true,
        childList: true,
      });
    }
  },
  // 对dom元素截图
  cropImg({ id }) {
    html2canvas(document.getElementById(id)).then((canvas) => {
      const a = document.createElement("a");
      a.href = canvas.toDataURL("image/png");
      a.download = `${Date.now()}.png`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    });
  },
};


使用

// template
<div id="water-mark"> </div>

// js 
import { watermark } from './watermark.js'

// 文本
const opts = {
  el: "water-mark",
  mark: "测试加水印"
  
};

watermark.init(opts);

// 图片
const opts = {
  el: "water-mark",
  imgUrl: 'http://127.0.0.1:8888/img.png',
  imgAttrs: [20, 20, 80, 80] // ctx.drawImage(img, ...imgAttrs);
};

watermark.init(opts)