常见的水印就是微博、抖音平台,转载内容时,会加上自己的名片信息,增加知名度。
这两天有时间看了下浏览器加水印的一些实现,总结下
水印是在浏览器元素的最上层级加上一个水印层 top-layer,
水印层是按照一定规则显示文本或图片
代码
// 废话少说上代码, 支持文本,图片,防篡改功能
// watermark.js
export const watermark = {
uuid: 0,
dom: null,
waterMarkDom: null,
defaultOpts: {
width: 200,
height: 200,
offsetLeft: 0,
offsetTop: 0,
angle: 30,
opacity: 0.5,
zIndex: 99999,
color: "
font: "18px Microsoft YaHei",
mark: "",
},
opts: {},
init: async function (opts) {
this.opts = { ...this.defaultOpts, ...opts }
this.opts.bgUrl = await this.generateText2WaterPic()
this.getEl()
this.addWaterMark()
this.observerMark()
},
generateImg2WaterPic: function () {},
// 生成水印图
generateText2WaterPic: function () {
const { height, width, angle, offsetLeft, offsetTop, color, font, mark, imgUrl = "", imgAttrs = [0, 0] } = this.opts
const can = document.createElement("canvas")
can.width = width
can.height = height
const ctx = can.getContext("2d")
if (imgUrl) {
return new Promise((resolve, inject) => {
var img = new Image()
img.src = imgUrl
img.onload = function (e) {
ctx.drawImage(img, ...imgAttrs)
// ctx.drawImage(img, sx, sy, swidth, sheight, x, y, width, height)
const src = can.toDataURL("image/png")
resolve(src)
}
})
} else {
// 高度默认是sin + 文字高度, 这样文字才完全显示出来
let h = Math.sin(angle / 360) * width + 18
ctx.rotate(-angle / 360)
ctx.font = font
ctx.fillStyle = color
ctx.textAlign = "left"
// 设置文字开始位置
ctx.fillText(mark, 0 + offsetLeft, h + offsetTop)
return can.toDataURL("image/png")
}
},
// 生成加水印的dom元素
addWaterMark: function () {
const { el, opacity, zIndex, bgUrl, left = 0, right = 0, top = 0, bottom = 0, bgSize = "auto" } = this.opts
const div = document.createElement("div")
this.uuid++
div.id = `water-mark-${this.uuid}`
let styles = {
"pointer-events": "none",
opacity: opacity,
"z-index": zIndex,
left: left + "px",
right: right + "px",
top: top + "px",
bottom: bottom + "px",
background: `url(${bgUrl}) left top repeat`,
"background-size": bgSize,
}
if (!el) {
styles.position = "fixed"
} else {
this.dom.style.position = "relative"
styles.position = "absolute"
}
let str = ""
Object.keys(styles).forEach((k) => {
str += `${k}: ${styles[k]}
})
this.opts.style = str
div.style = str
this.waterMarkDom = div
this.dom.appendChild(div)
},
// 获取添加水印的dom元素,如果没有写,默认加到body上
getEl: function () {
const { el } = this.opts || this.defaultOpts
let dom
if (!el) {
dom = document.body
} else {
dom = document.getElementById(el)
}
if (!dom) {
dom = document.body
}
this.dom = dom
},
// 监测水印防止删除,或改动
observerMark() {
const { style } = this.opts
const body = document.body
const waterMarkDom = this.waterMarkDom
const _this = this
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver
if (MutationObserver) {
let mo = new MutationObserver(function () {
//
if ((waterMarkDom && waterMarkDom.getAttribute("style") !== style) || !waterMarkDom) {
// 避免一直触发
mo.disconnect()
mo = null
_this.addWaterMark()
_this.observerMark()
}
})
mo.observe(body, {
attributes: true,
subtree: true,
childList: true,
})
}
},
// 对dom元素截图
cropImg({ id }) {
html2canvas(document.getElementById(id)).then((canvas) => {
const a = document.createElement("a")
a.href = canvas.toDataURL("image/png")
a.download = `${Date.now()}.png`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
})
},
}
使用
<div id="water-mark"> </div>
import { watermark } from './watermark.js'
const opts = {
el: "water-mark",
mark: "测试加水印"
};
watermark.init(opts);
const opts = {
el: "water-mark",
imgUrl: 'http://127.0.0.1:8888/img.png',
imgAttrs: [20, 20, 80, 80]
};
watermark.init(opts)