类似antd5 的水印,可防止删除、修改水印元素
type TWatermark = {
/** 渲染水印的同时并创建水印被修改的观察器 */
init: () => void
/** 卸载观察器 */
uninstallObserver: () => void
}
type WatermarkConfig = {
/** 水印盒子元素 */
wrapperElement: HTMLElement,
/** 字体大小 */
fontSize?: number,
/** 水印之间的间隙 */
gap?: number,
/** 水印文字 */
text?: string
}
class Watermark implements TWatermark {
protected observer: MutationObserver;
protected watermarkElement: HTMLElement | null;
protected config: Required<WatermarkConfig>
constructor(config: WatermarkConfig) {
this.config = {
wrapperElement: config.wrapperElement,
fontSize: config.fontSize || 20,
gap: config.gap || 10,
text: config.text || 'watermark'
};
this.watermarkElement = null;
this.observer = new MutationObserver(this.handleMutation.bind(this));
}
// 渲染水印的同时并创建水印被修改的观察器
public init() {
if (!this.config.wrapperElement) {
return
}
// 将传入的水印盒子设置定位,后边会将水印添加到这个元素中
this.config.wrapperElement.style.position = 'relative';
// 渲染水印
this.resetWatermark()
// 观察传入的元素也就是水印的父元素
this.observer.observe(this.config.wrapperElement, {
childList: true,// 观察子节点
subtree: true,// 观察所有的后代节点
attributes: true,// 观察所有属性变化
});
}
// 卸载观察器
public uninstallObserver() {
this.watermarkElement = null
this.observer.disconnect();
}
// 修改后执行的方法
private handleMutation(entries: MutationRecord[]) {
for (const entry of entries) {
// 删除
for (const node of entry.removedNodes) {
// 如果删除的元素是水印元素
if (node === this.watermarkElement) {
this.resetWatermark();
}
}
// 修改,如果修改的元素是水印元素
if (entry.target === this.watermarkElement) {
this.resetWatermark();
}
}
}
// canvas 生成 水印背景
private watermarkBg() {
const canvas = document.createElement('canvas');
const devicePixelRatio = window.devicePixelRatio || 1;
const fontSize = this.config.fontSize * devicePixelRatio;
const font = fontSize + 'px "Microsoft YaHei", sans-serif';
const ctx = canvas.getContext('2d');
if (!ctx) {
return null
}
ctx.font = font;
const { width } = ctx.measureText(this.config.text);
const canvasSize = Math.max(100, width) + this.config.gap * devicePixelRatio
canvas.width = canvasSize;
canvas.height = canvasSize;
ctx.translate(canvasSize / 2, canvasSize / 2);
ctx.rotate((Math.PI / 180) * 45);
ctx.fillStyle = 'rgba(0,0,0,0.3)';
ctx.font = font;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(this.config.text, 0, 0);
return {
base64: canvas.toDataURL('image/png'),
size: canvasSize / devicePixelRatio
}
}
private resetWatermark() {
if (!this.config.wrapperElement) {
console.log('未获取到父元素');
return
}
// 由于监听元素变化后会重新创建,此处做判断如果有水印元素则要删除重新创建,防止水印元素重复创建
if (this.watermarkElement) {
this.watermarkElement.remove();
}
const bg = this.watermarkBg()
if (!bg) {
return
}
const { base64, size } = bg
this.watermarkElement = document.createElement('div');
this.watermarkElement.style.position = 'absolute';
this.watermarkElement.style.backgroundImage = `url(${base64})`;
this.watermarkElement.style.backgroundSize = `${size}px ${size}px`;
this.watermarkElement.style.backgroundRepeat = 'repeat';
this.watermarkElement.style.zIndex = '9999'
this.watermarkElement.style.pointerEvents = 'none';
this.watermarkElement.style.inset = '0'
this.config.wrapperElement.appendChild(this.watermarkElement);
}
}
export default Watermark
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。