100行代码实现一个无法删除的水印

1,491 阅读4分钟


一个无法删除的水印组件


完整源码:github.com/wstreet/wat…
查看效果:wstreet.vip
Canvas学习:www.yuque.com/streetex/fb…

Installation

# use npm
npm install @wstreet7/watermark --save

# or use yarn
yarn add @wstreet7/watermark

# or script
<script src="https://unpkg.com/@wstreet7/watermark@latest/lib/watermark.min.js"></script>

Usage


Simple Example

import WaterMark from '@wstreet7/watermark';

const config = {
    text: '@wstreet7',
    height: 60,
    width: 100
}
const waterMark = new WaterMark(config)

Config

字段 描述 默认值
id container的id water-mark
height 每个repeat单元的height 100
width 每个repeat单元的width 150
font 字体相关,font-size和font-family必须包含 10px sans-serif   其他配置
rotate 文本倾斜角度 -30(canvas 正旋转为顺时针)
www.yuque.com/streetex/fb…
fillStyle 文本颜色 #ccc
opacity 透明度 1.0
zIndex z-index 9999



实现思路


1、绘制canvas并将canvas转换成图片设置为背景
2、监听dom水印是否被删除或者修改,如果是,则再执行第1步


生成canvas并转换成图片很好理解,这里讲一下监听dom被修改的逻辑


水印是直接插入在body下的,需要监听watermark dom和body:
监听watermark dom如果被修改(childList,attributes,characterData),直接删除就行
监听body childList的变化,如果是删除了水印,则再绘制一次



const MutationObserver = window.MutationObserver 
                      || window.WebKitMutationObserver 
                      || window.MozMutationObserver;

class WaterMark {
  defaultOpts = {
    id'water-mark',
    height100,
    width150,
    font'10px sans-serif',
    rotate-30,
    fillStyle'#ccc',
    opacity1.0,
    text'',
    zIndex9999
  }

  constructor(options) {
    this._cfg = {...this.defaultOpts, ...options}

    this.createObserver()
    this.createCanvas()
    this.load()
    
  }


  set(key, value) {
    this._cfg[key] = value
  }

  get(key) {
    return this._cfg[key]
  }

  createObserver() {
    this.createDomObserver()
    this.createBodyObserver()
  }

  // 水印的载体div
  createDomObserver() {
    this.observer = new MutationObserver(() => {
      this.remove()
    });
  }


  createBodyObserver() {
    this.bodyObserver = new MutationObserver((mutationList) => {
      if (
          mutationList[0].removedNodes.length && 
          mutationList[0].removedNodes[0].id === this.get('id')
        ) {
        this.load()
      }
    });
  }


  createCanvas() {
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    const height = this.get('height')
    const width = this.get('width')
    canvas.height = height
    canvas.width = width

    const font = this.get('font')
    const text = this.get('text')
    const rotate = this.get('rotate')
    const fillStyle = this.get('fillStyle')
    const opacity = this.get('opacity')
    
    ctx.fillStyle = fillStyle
    ctx.font = font;
    ctx.globalAlpha = opacity;
    ctx.rotate(rotate / Math.PI * 2)
    ctx.translate(0, height / 2)
    ctx.fillText(text, 0, height / 2 )
    
    this.set('canvas', canvas)
    this.set('ctx', ctx)
  }

  observe() {
    this.domObserve()
    this.bodyObserve()
  }

  domObserve() {
    const dom = this.get('dom');
    this.observer.observe(dom, {
      childListtrue,
      attributestrue,
      characterDatatrue,
    })
  }

  bodyObserve() {
    const body = document.querySelector('body')
    this.bodyObserver.observe(body, {
      childListtrue
    })
  }


  remove() {
    const body = document.querySelector('body')
    const dom = this.get('dom')
    body.removeChild(dom)
    this.set('dom'null)
  }
   

  load() {
    const canvas = this.get('canvas')
    const img = canvas.toDataURL('image/png'1.0)
    const body = document.querySelector('body')
    const dom = document.createElement('div')
    dom.setAttribute('id'this.get('id'))
    dom.setAttribute('style'
      `
      background-image:url(${img}); 
      height: 100%; 
      position: fixed; 
      left: 0; 
      top: 0; 
      bottom: 0; 
      right: 0;
      pointer-events: none;
      z-index: ${this.get('zIndex')}
      `

    )
    this.set('dom', dom)
    body.appendChild(dom)
    this.observe()
  }
}

export default WaterMark

效果图