React有Antd的Watermark水印组件,那Vue如何实现一个水印组件

727 阅读4分钟

“我正在参加「掘金·启航计划」”

1、思路

1.1、如何生成水印

  • 在需要加水印的区域,加div元素,且让该div元素填充满该区域,再给这个div元素一个背景图,并让这个背景图重复铺满需要水印的区域。而这个背景图可使用canvas来绘制,绘制一个透明、可以根据传入的字体大小、间隔大小绘制的图片

1.2、如何防止篡改

  • 这里需要MutationObserverwatchEffect结合使用
    • MutationObserver:提供了监视对DOM树所做更改的能力,在DOM被修改时,修改watchEffect收集的依赖项
    • watchEffect:依赖项被修改时,再次执行
    • MutationObserver可点击查看其用法

2、实现

2.1、实现一个水印组件

  • 在这个水印组件中,可以接受三个值:textfontSizegap,除此之外,也可以根据实际需求添加所需要的值
    • text:是水印文字
    • fontSize:是水印文字的字体大小
    • gap:是生成的水印图片间隔大小
const props = defineProps({
  text: {
    type: String,
    required: true,
    default: 'WaterMark'
  },
  fontSize: {
    type: Number,
    default: 40
  },
  // 间隙
  gap: {
    type: Number,
    default: 20
  }
})
  • 在这个组件中,我们并不能直接在template中直接添加div元素,因为:在template中直接添加div元素,被删除后,不可以被重新生成,所以我们要用ts去创建div元素,也不能在class中直接添加样式,道理也同上,需要使用ts来添加样式
  • 因为需要让水印铺满,所以可以使用inset属性,设置为0,则四边到父元素的距离都为0
const bg = useWatermarkBg(props)
const parentRef: any = ref(null)

let div: any
watchEffect(() => {
  if (!parentRef.value) {
    return
  }
  // 如果节点有值,进行删除
  if (div) {
    div.remove()
  }
  const { base64, styleSize } = bg.value

  div = document.createElement('div')
  div.style.backgroundImage = `url(${base64})`
  div.style.backgroundSize = `${styleSize} px ${styleSize}px`
  div.style.backgroundRepeat = 'repeat'
  div.style.zIndex = '9999'
  div.style.position = 'absolute'
  div.style.inset = '0'
  div.style.pointerEvents = 'none'

  parentRef.value.appendChild(div)
})
  • 上述代码中使用到一个生成canvashook函数useWatermarkBg,这个函数,返回了base64sizestyleSize三个值
  • base64就是水印图片地址,若为了水印图更加清晰,我们选择使用styleSize,而为什么使用styleSize,有空在下一篇讲解,本篇就不做更多介绍canvas
  • useWatermarkBg将在最后和全部代码一起给出
  • 这时已经可以生成水印,但是仍然可以被删掉水印,如下图

2023-04-29 14.16.41.gif

2.2、实现防止篡改水印(删除)

  • 防止篡改有两步:
    • 1、使用MutationObserver进行监控元素的变化(本身和监控元素里的所有子元素)
    • 2、使用watchEffect重新生成
  • 在这里就需要使用上文提到的MutationObserver了,我们需要创建监听器new MutationObserver()observer = new MutationObserver()
  • new好之后,我们会使用到以下两个方法
    • observe:监听器,接收两个参数。1是传入监听的节点,2是相关配置,比如:
    observer.observe(parentRef.value, {
      // 元素内容
      childList: true,
      // 本身属性
      attributes: true,
      // 整个子树
      subtree: true
    })
    
    • disconnect:停止监听,在DOM卸载时,便要停止监听
    onUnmounted(() => {
        observer && observer.disconnect() // 取消监听
    })
    
  • 我们试着去删除水印,看看是否可以监听到这个操作

2023-04-29 15.04.07.gif

  • 可以看到的是,我们删除水印,触发了监听:removedNodes: [div] image.png

  • 我们试着去修改样式,看看是否可以监听到这个操作

2023-04-29 15.15.03.gif

  • 可以看到的是,我们修改了水印样式,触发了监听:attributename: "style" image.png

  • 那为什么在gif图中水印并没有被删除成功以及水印样式没有被修改成功呢?其实,水印已经被删除过一次以及样式已经被修改过了。

  • 而再次出现是因为,在我的代码中已将MutationObserverwatchEffect结合使用了,且在监听到删除操作或者修改操作时,将watchEffect的依赖项(flag.value++)进行修改,watchEffect监听到依赖项的改变,包含的方法就会再次执行,这样水印就会重新出现

let observer: any
onMounted(() => {
  // 创建监听器
  observer = new MutationObserver((records) => {
    console.log(records)
    for (const record of records) {
      // 删除水印
      for (const dom of record.removedNodes) {
        if (dom === div) {
          // 有删除操作
          console.log('删除了水印')
          flag.value++
          return
        }
      }

      // 监控修改
      if (record.target === div) {
        console.log('修改了水印样式')
        flag.value++

        return
      }
    }
  })
  // 监听谁--父元素
  observer.observe(parentRef.value, {
    // 监听配置:监听什么
    // 元素内容
    childList: true,
    // 本身属性
    attributes: true,
    // 整个子树
    subtree: true
  })
})
  • 水印效果如图:
    • 左边一张图片,右边一段文字,也可以给视频添加水印

image.png

  • 需要代码可访问github获取