js实现的前端水印

发布于:2025-04-19 ⋅ 阅读:(17) ⋅ 点赞:(0)

效果
在这里插入图片描述

var defaultConfig = {
  content: [], // 水印内容数组
  fontSize: 14, // 字体大小(px)
  fontFamily: 'sans-serif', // 字体
  color: 'rgba(255, 255, 255,.3)', // 字体颜色
  rotate: -20, // 旋转角度(度数)
  zIndex: 2147483647, // 层级
  globalAlpha: 0.5, // 透明度
  canvasWidth: 300, // 单个水印canvas宽度
  canvasHeight: 200, // 单个水印canvas高度
  textAlign: 'left', // 文本对齐方式
  top: 30, // 上边距
  left: 50, // 左边距
  container: document.body, // 挂载容器
  monitor: true //是否开启监控
}

function Watermark(options) {
  if (Watermark.instance) {
    Watermark.instance.destroy()
  }

  if (typeof options.content === 'string') {
    var timestamp = formatTime(new Date())
    options.content = [options.content, timestamp]
  }

  this.config = mergeObjects({}, defaultConfig, options || {})
  this.observers = []
  this.canvas = null
  this.watermarkDiv = null
  Watermark.instance = this

  this.init()
}

Watermark.prototype.init = function () {
  this.createCanvas()
  this.applyWatermark()
}

Watermark.prototype.createCanvas = function () {
  var config = this.config
  var canvas = document.createElement('canvas')
  var ctx = canvas.getContext('2d')
  canvas.width = config.canvasWidth
  canvas.height = config.canvasHeight
  ctx.save()

  try {
    ctx.font = config.fontSize + 'px ' + config.fontFamily
    ctx.fillStyle = config.color
    ctx.globalAlpha = config.globalAlpha
    ctx.textAlign = config.textAlign

    var centerX = canvas.width / 2
    var centerY = canvas.height / 2

    ctx.translate(centerX, centerY)
    ctx.rotate((config.rotate * Math.PI) / 180)
    ctx.translate(-centerX, -centerY)

    var lineHeight = config.fontSize * 1.5
    var startY = (canvas.height - config.content.length * lineHeight) / 2

    config.content.forEach(function (text, index) {
      ctx.fillText(text, config.left, startY + index * lineHeight + config.top)
    })
  } finally {
    ctx.restore()
  }

  this.canvas = canvas
}

Watermark.prototype.applyWatermark = function () {
  var config = this.config
  var container = this.getContainer()
  var isBodyContainer = container === document.body // 判断是否为body容器

  // 创建水印层
  var dom = document.createElement('div')
  dom.style.position = isBodyContainer ? 'fixed' : 'absolute'
  dom.style.top = '0'
  dom.style.left = '0'
  dom.style.width = '100%'
  dom.style.height = '100%'
  dom.style.pointerEvents = 'none'
  dom.style.zIndex = config.zIndex
  dom.style.backgroundImage = 'url(' + this.canvas.toDataURL() + ')'
  dom.style.backgroundRepeat = 'repeat'

  // 处理容器定位
  if (
    !isBodyContainer &&
    window.getComputedStyle &&
    window.getComputedStyle(container).position === 'static'
  ) {
    container.style.position = 'relative'
  }

  this.destroyInternal()
  this.watermarkDiv = dom
  container.appendChild(dom)

  // 防止篡改
  if (this.config.monitor) {
    this.setupMutationObserver()
  }
}

Watermark.prototype.getContainer = function () {
  var container = this.config.container
  if (typeof container === 'string') {
    return document.querySelector(container) || document.body
  }
  return container || document.body
}

Watermark.prototype.setupMutationObserver = function () {
  var self = this
  if (typeof MutationObserver === 'undefined') return

  // 水印节点监听
  var primaryObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      if (mutation.type === 'attributes') {
        self.applyWatermark()
      }
    })
  })

  primaryObserver.observe(this.watermarkDiv, {
    attributes: true,
    attributeFilter: ['style', 'class'],
    childList: false,
    subtree: false
  })

  // 父节点监听
  var guardianObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      if (mutation.removedNodes && mutation.removedNodes.length > 0) {
        if (
          [].some.call(mutation.removedNodes, function (node) {
            return node === self.watermarkDiv
          })
        ) {
          self.applyWatermark()
        }
      }
    })
  })

  guardianObserver.observe(this.watermarkDiv.parentNode, {
    childList: true,
    subtree: false
  })

  this.observers.push(primaryObserver, guardianObserver)
}

Watermark.prototype.destroy = function () {
  this.destroyInternal()

  Watermark.instance = null
}

Watermark.prototype.destroyInternal = function () {
  // 移除水印元素
  if (this.watermarkDiv && this.watermarkDiv.parentNode) {
    this.watermarkDiv.parentNode.removeChild(this.watermarkDiv)
  }

  // 停止观察
  this.observers.forEach(function (observer) {
    observer.disconnect()
  })
  this.observers = []
}

// 对象合并方法(浅拷贝)
function mergeObjects(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i]
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key]
      }
    }
  }
  return target
}

// 日期格式化
function formatTime(date) {
  var year = date.getFullYear()
  var month = date.getMonth() + 1
  var day = date.getDate()
  var hours = date.getHours()
  var minutes = date.getMinutes()
  var seconds = date.getSeconds()

  month = month < 10 ? '0' + month : month
  day = day < 10 ? '0' + day : day
  hours = hours < 10 ? '0' + hours : hours
  minutes = minutes < 10 ? '0' + minutes : minutes
  seconds = seconds < 10 ? '0' + seconds : seconds

  return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}

export default Watermark

Vue3集成方式

import Watermark from "@/utils/watermark.esm.js";

// 初始化水印
const watermark = ref(null);
onMounted(() => {
  watermark.value = new Watermark ({
    content: "张三"
  });
});

// 销毁水印(可选)
onBeforeUnmount(() => {
  watermark.value?.destroy();
});```






网站公告

今日签到

点亮在社区的每一天
去签到