效果
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();
});```