当前位置: 首页 > article >正文

JS 生成防篡改水印

网页中有水印的需求,今天我们实现手写一个防篡改水印,先看下效果图:

在这里插入图片描述

一、创建class函数

传递一个dom为水印包裹器,有一些监听防篡改的observer,然后实例化的时候创建水印,执行create()方法

class WaterMarker {
  private container: HTMLElement | null;
  private observer_c?: MutationObserver; // 监听水印,防篡改
  private observer_p?: MutationObserver; // 监听水印,防删除
  constructor(container: HTMLElement || null) {
    this.container = container
    this.create() // 初始化
  }
 
 
  // 获取水印元素
  getElement(): HTMLElement | null {
    return document.getElementById(`water_marker`)
  }

  // 延时执行
  async wait(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms))
  }
}

let dom = document.querySelector('#body') as HTMLElement || null
dom ? let waterMarker = new WaterMarker(dom) : ''

二、创建水印dom

这里创建水印dom记得将鼠标指针设置为无效,以免水印dom遮挡画面部分点击区域,‘pointerEvents’ 为 null,这里的 mixBlendMode 混合模式则是为了避免水印和画面的颜色重叠,造成水印不可见。

// 创建水印
  async create(): Promise<void> {
    await this.wait(500)
    let content = `<span style="font-size: 18px">游客</span> <br> <span style="font-size: 18px">Guest</span> <br> 中国传媒大学`
    let el = document.createElement('div') as HTMLElement
    el.style.display = 'inline-block'
    el.style.position = 'absolute'
    el.id = `water_marker`
    el.style.top = '20px'
    el.style.left = '20px'
    el.style.lineHeight = '20px'
    el.style.color = '#fff'
    el.style.fontSize = '14px'
    el.style.textAlign = 'center'
    el.style.fontWeight = 'bold'
    el.style.pointerEvents = 'none'
    el.style.filter = 'opacity(0.75)'
    el.style.textShadow = '1px 1px 0px rgba(0, 0, 0, 0.2),\n' +
      '      1px 2px 0px rgba(0, 0, 0, 0.2),\n' +
      '      1px 3px 0px rgba(0, 0, 0, 0.2)'
    el.style.mixBlendMode = 'difference'
    el.style.zIndex = 10
    el.style.transform = 'rotateZ(-45deg)'
    el.innerHTML = content
    this.container?.appendChild(el)
    // 开始动画
    this.createAnimate(el)
    // 等待一秒开始监听水印篡改,删除
    await this.wait(1000)
    // 监听水印,防篡改
    this.observeC(el)
    // 监听水印,防删除
    this.observeP()
  }

三、给水印添加animate动画,旋转动画

 // 创建动画
  createAnimate(el: HTMLElement): void {
    let delay = 1000 * 6 // 6秒循环一次
    let width = this.container.offsetWidth || 0
    let height = this.container.offsetHeight || 0

    el.animate(
      [
        { transform: `translate(0 ,0) rotateZ(-45deg)` }, // 起始位置
        { transform: `translate(calc(${width - 40}px - 100%), 0) rotateZ(45deg)`}, // 右侧
        { transform: `translate(calc(${width - 40}px - 100%), calc(${height - 40}px - 100%)) rotateZ(45deg)` }, // 底部
        { transform: `translate(0, calc(${height - 40}px - 100%)) rotateZ(-45deg)` }, // 左侧
        { transform: `translate(0, 0) rotateZ(-45deg)`} // 返回起始位置
      ],
      {
        duration: delay,
        iterations: Infinity, // 无限循环
        easing: 'cubic-bezier(.31,.01,1,1.27)',
        delay: 0,
      }
    )
  }

四、水印添加防篡改,防删除

防篡改用MutationObserver监听目标元素的节点、属性、子节点变化达到防篡改的作用
防删除用MutationObserver监听包裹器,判断删除的子节点是否包含水印,达到防删除的作用

这里的option配置有几个属性:

属性名作用
attributes元素节点的属性值变化
characterData元素节点的内容数据发生变化
childList元素节点的子节点的新增和移除
subtree元素深层节点的变化
 // 监听目标元素的变化
  observeC(el: HTMLElement): void {
    let config: MutationObserverInit = { attributes: true, characterData: true, childList: true, subtree: true }
    this.observer_c = new MutationObserver((mutationsList: MutationRecord[]) => {
      for (let mutation of mutationsList) {
        switch (mutation.type) {
          case 'attributes':
          case 'childList':
          case 'characterData':
            alert('请勿篡改水印哦,尊重版权!')
            // 销毁重新创建,这里可以函数防抖下,改变style的时候
            let cb = () => {
             this.destroy()
             this.create()
            }
            debounce(cb, 100)
            break;
          default:
            break;
        }
      }
    });
    this.observer_c.observe(el, config);
  }

  // 监听目标元素的父元素的子节点删除,从而达到监听水印被删除
  observeP() {
    let config: MutationObserverInit = { attributes: true, characterData: true, childList: true, subtree: true }
    this.observer_p = new MutationObserver((mutationsList: MutationRecord[]) => {
      for (let mutation of mutationsList) {
        switch (mutation.type) {
          case 'childList':
          	// 判断删除的子节点中是否包含水印节点
            let removedNodes = Array.from(mutation.removedNodes)
            if (removedNodes.find(node => node.id.includes('water_marker'))) {
              alert('请勿篡改水印哦,尊重版权!')
              // 销毁重新创建
              this.destroy()
              this.create()
            }
            break;
          default:
            break;
        }
      }
    });
    this.observer_p.observe(this.container, config);
  }

五、添加销毁事件

这里要先销毁监听器,再销毁水印dom

  destroy() {
    this.observer_c?.disconnect()
    this.observer_p?.disconnect()
    let el: HTMLElement = this.getElement()
    el? el.remove() : ''
  }

六、拓展

如果涉及到重复,创建dom可修改为canvas创建,这里就不多说了


http://www.kler.cn/a/443080.html

相关文章:

  • scrapy爬取图片
  • rk3568 , buildroot , qt ,使用sqlite, 动态库, 静态库
  • L1G5000 XTuner 微调个人小助手认知
  • 【LeetCode】力扣刷题热题100道(21-25题)附源码 接雨水 合并区间 字母异位词 滑动窗口 覆盖子串(C++)
  • Linux第一课:c语言 学习记录day06
  • socket网络编程-TC/IP方式
  • OCR多模态大模型:视觉模型与LLM的结合之路
  • PDFMathTranslate 一个基于AI优秀的PDF论文翻译工具
  • 知识库管理系统可扩展性深度测评
  • 【论文笔记】Visual Prompt Tuning
  • 《自制编译器》--青木峰郎 -读书笔记 编译hello
  • 三维测量与建模笔记 - 7.2 点云滤波
  • mapper.xml传入参数为Map的正确做法
  • springboot使用scoket
  • C#速成(文件读、写操作)
  • Vite打包后动态路由加载失败的问题
  • springboot集成h2数据库并使用多数据源
  • AG32 IDE 开发环境搭建
  • 大模型微调---Prompt-tuning微调
  • spring\strust\springboot\isp前后端那些事儿
  • C# 中的Task
  • YOLOv5-7.0训练过程中出现报错Example: export GIT_PYTHON_REFRESH=quiet
  • 在JVM(Java虚拟机)中,PC寄存器(Program Counter Register)扮演着至关重要的角色,它是JVM执行引擎的核心组成部分之一。
  • Java开发者的神经网络进阶指南:深入探讨交叉熵损失函数
  • 【Linux基础】基本开发工具的使用
  • Spring Cloud Sleuth 分布式链路追踪入门