防改水印
利用 canvas 创建文字背景,然后生成 mark 容器。需要使用的地方,插入这个容器,监听这个父容器的修改,然后做重新生成容器。
实现
ts
type optionsType = {
text: string;
fontSize: number;
color: string;
gap: number;
opacity: number;
font?: string;
};
type cnavasDataType = {
base64: string;
size: number;
styleSize: number;
opacity: number;
};
const createCanvasText = (options: optionsType) => {
const canvas = document.createElement("canvas");
const devicePxielRatio = window.devicePixelRatio || 1;
const fontSize = (options.fontSize || 16) * devicePxielRatio;
const font = fontSize + `px ${options.font || "serif"}`;
const ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.font = font;
const { width } = ctx.measureText(options.text || "mark"); // 获取文本宽度信息
const canvasSize = Math.max(100, width) + options.gap * devicePxielRatio;
canvas.width = canvasSize;
canvas.height = canvasSize;
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate((Math.PI / 180) * -45);
ctx.fillStyle = options.color;
ctx.font = font;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(options.text || "mark", 0, 0);
return {
base64: canvas.toDataURL(),
size: canvasSize,
styleSize: canvasSize / devicePxielRatio,
opacity: options.opacity,
};
};
const creatSectionWrap = (canvasText: cnavasDataType) => {
const section = document.createElement("section");
section.style.backgroundImage = `url(${canvasText?.base64})`;
section.style.backgroundSize = `${canvasText?.styleSize}px ${canvasText?.styleSize}px `;
section.style.backgroundRepeat = "repeat";
section.style.zIndex = "99999999";
section.style.position = "absolute";
section.style.inset = "0";
section.style.opacity = (canvasText.opacity || 1) + "";
return section;
};
export const createWatermark = (options: optionsType) => {
const canvasText = createCanvasText(options) as cnavasDataType;
let ob: MutationObserver;
let p: HTMLElement;
let section: HTMLElement | null;
return {
create: (parent: HTMLElement) => {
section = creatSectionWrap(canvasText);
parent.appendChild(section);
p = parent;
ob = new MutationObserver((records) => {
for (const el of records) {
if (el.type === "attributes") {
if (el.target === section) {
parent.removeChild(section);
return;
}
}
if (
el.type === "childList" &&
el.removedNodes.length &&
el.target === parent
) {
el.removedNodes.forEach((e) => {
if (e === section) {
section = creatSectionWrap(canvasText);
parent.appendChild(section);
}
});
}
}
});
ob.observe(parent, {
childList: true,
attributes: true,
subtree: true,
});
},
remove: () => {
ob && ob.disconnect();
section && p.removeChild(section);
section = null;
},
clear: () => {
ob && ob.disconnect();
section = null;
},
};
};
export default createWatermark;使用
js
const wrap = ref<HTMLElement>()
const { create, clear, remove } = createWatermark({ fontSize: 20, text: "maps1313", color: 'red', gap: 20, opacity: 0.6 })
const createMark = () => {
if (!wrap.value) return
create(wrap.value)
}
onBeforeUnmount(() => {
clear()
})
JStar