水印插件

水印

const init = Symbol('init');

export default class WaterMask {
  [init] = () => {
    const {
      container = document.body,
      isRem = true,
      content = '01211115',
      width = '160px',
      color = '#97989C',
      height = '180px',
      opacity = '0.2',
      fontSize = '14px',
      zIndex = 1000,
    } = this.options;
    document.documentElement.style.height = '100%';
    function pxRemScale(size) {
      return isRem
        ? (size.replace('px', '') / 16) *
            (document.documentElement.style.fontSize.replace('px', '') || 14) *
            1 +
            'px'
        : size;
    }
    const svgStr = `<svg xmlns="http://www.w3.org/2000/svg" width="${pxRemScale(
      width,
    )}" height="${pxRemScale(height)}">
                        <text x="50%" y="50%" dy="12px"
                            text-anchor="middle"
                            fill="${color}"
                            fill-opacity="${opacity}"
                            transform="rotate(-45, 120 120)"
                            style="font-size: ${pxRemScale(fontSize)};">
                            ${content}
                        </text>
                    </svg>`;
    const base64Url = `data:image/svg+xml;base64,${window.btoa(
      unescape(encodeURIComponent(svgStr)),
    )}`;
    const waterMask = document.querySelector('.water-mask');

    const watermarkDiv = waterMask || document.createElement('div');
    watermarkDiv.className = 'water-mask';
    watermarkDiv.setAttribute(
      'style',
      `position:absolute;
    top:0;
    left:0;
    width:${document.documentElement.offsetWidth}px;
    height:${document.documentElement.offsetHeight}px;
    z-index:${zIndex};
    pointer-events:none;
    background-repeat:repeat;
    background-position:0;
    background-image:url('${base64Url}')`,
    );
    container.style.position = 'relative';
    container.appendChild(watermarkDiv);
    this.bindMutationOberve();
    this.bindWindowResizeEvent();
  };
  constructor(
    options = {
      container: document.body,
      isRem: false,
      content: '请勿外传',
      width: '300px',
      height: '200px',
      opacity: '0.2',
      fontSize: '28px',
      zIndex: 1000,
    },
  ) {
    this.options = options;
    this[init]();
  }
  bindWindowResizeEvent = () => {
    window.addEventListener('resize', this.adjust);
  };
  adjust = () => {
    window.requestAnimationFrame(() => {
      const watermarkDiv = document.querySelector('.water-mask');
      watermarkDiv.style.width = `${document.documentElement.offsetWidth}px`;
      watermarkDiv.style.height = `${document.documentElement.offsetHeight}px`;
    });
  };
  bindMutationOberve = () => {
    let config = {
      childList: true,
      attributes: true,
      characterData: true,
      subtree: true,
      attributeOldValue: true,
      characterDataOldValue: true,
    };

    const mutationCallback = mutationList => {
      for (let mutation of mutationList) {
        let type = mutation.type;
        switch (type) {
          case 'childList':
            if (!document.querySelector('.water-mask')) {
              console.log('水印节点被移除,上报服务器或警告');
            }
            break;
          case 'attributes':
            if (!document.querySelector('.water-mask')) {
              console.log('水印节点被移除,上报服务器或警告');
            }
            break;
          case 'subtree':
            console.log('子树被修改');
            break;
          default:
            console.log(type);
            break;
        }
      }
    };

    // 创建 MutationObserver 实例
    this.observer = new MutationObserver(mutationCallback);

    // 开始监控目标节点
    this.observer.observe(document.body, config);
  };
  destroy = () => {
    const watermarkDiv = document.querySelector('.water-mask');
    watermarkDiv.remove();
    window.removeEventListener('resize', this.adjust);
    this.observer.disconnect();
  };
}
  • 首先声明一个WaterMask类,通过这个类生成一个实例化对象,在初始化实例对象时,传一些必要属性;比如:水印内容,根元素(document.body),水印样式,透明度等。
  • 函数pxRemScale是用于适配性修改字体大小,此处用到rem单位,即相对于根元素的字体大小进行比例缩放;
  • 生成一个svg图片对象,然后手动生成一个base64的图片,生成一个div元素,然后将该图片作为背景图片,设置绝对定位定位属性postition:abosultebackground-repeat:repeat该属性可以将文本重复渲染到全屏;
  • 设置body元素position属性,然后将div元素加入body中,此时让div元素充满整个body,并且设置最高的层级z-index需要注意的是,此时整个应用有一层透明蒙层在,需要设置pointer:none,设置事件穿透效果;
  • 创建 MutationObserver 实例,用于监听div元素的改动,如果改动就进行恢复或者警示;
  • 【注意:MutationObserver 是一个微任务】this.observer = new MutationObserver(mutationCallback);
  • 最后可以设置一个水印销毁方法,可配置化的设置水印;
  • 监听this.observer.observe(document.body, config);
  • 取消监听:this.observer.disconnect();
总结

1.生成一个svg对象,这个对象可以作为新建元素div的背景图片
2.将该div元素放在根元素中,通过设置position:absolute属性让元素充满整个body;注意:绝对定位元素会脱离文档流,因此可以铺满整个父元素,设置较高的层级值z-index,但是需要设置透明属性,以及pointer:none,事件穿透效果;
3.获取根元素的fontsize,用rem动态的设置字体大小,保证字体的适配性;
4.创建 MutationObserver 实例监听div元素的改动,如果改动则恢复设置或者警示;
5.使用方法,初始化一个水印实例对象,初始化的时候将必要的参数传入,给到div元素使用,比如:水印内容,蒙层透明度,根元素,样式等

知识点
  • position定位
  • 元素的添加,移除,设置事件穿透,background-repeat:repeat重复渲染铺满全屏;
  • 根元素的fontsize,rem单位适配性问题,px,em区别;
  • 创建 MutationObserver 实例监听div元素的改动,微任务;

参考:前端生成水印方法
https://mp.weixin.qq.com/s/-FVRx0uwq2UUsXuJty6OIg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值