水印要求
要求实现多行水印展示效果,并且不可被篡改,水印内容可配置
实现效果
实现思路
1.首先需要一个水印生成的hook
2.需要一个watermark.vue组件
3.将watermark组件使用到需要使用的地方
代码如下:
1.hook(useWatermarkBg.js)
import { computed } from "vue";
export default function useWatermarkBg(props) {
// console.log(props);
return computed(() => {
// 创建一个 canvas
const canvas = document.createElement("canvas");
const devicePixelRatio = window.devicePixelRatio || 1;
// 设置字体大小
const fontSize = props.fontSize * devicePixelRatio;
const font = fontSize + "px serif";
const ctx = canvas.getContext("2d");
// 获取文字宽度
ctx.font = font;
const { width } = ctx.measureText(props.text);
const canvasSize = Math.max(200, width) + props.gap * devicePixelRatio;
canvas.width = canvasSize;
canvas.height = canvasSize;
ctx.translate(canvas.width / 2, canvas.height / 2);
// 旋转 45 度让文字变倾斜
ctx.rotate((Math.PI / 180) * -25);
ctx.fillStyle = "rgba(0, 0, 0, 0.25)";
ctx.font = font;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// 获取行高
const lineHeight = 21;
// 计算水印在y轴上的初始位置
let initY = (canvas.height - (16 * props.text.length + (props.text.length - 1) * 5)) / 2;
initY = initY < 0 ? 0 : initY;
for (let i = 0; i < props.text.length; i++) {
if (props.text[i] && props.text[i] != "") {
ctx.fillText(props.text[i], canvas.width / props.text.length, initY + lineHeight * i);
}
}
// 将需要返回的内容return出去
return {
base64: canvas.toDataURL(), // 返回一个base64格式水印图片
size: canvasSize,
styleSize: canvasSize / devicePixelRatio
};
});
}
2.watermark.vue组件
<template>
<div class="watermark-container" ref="parentRef">
<slot></slot>
</div>
</template>
<script setup>
import { onMounted, onUnmounted, ref, watchEffect } from "vue";
import useWatermarkBg from "./useWatermarkBg";
import pinia from "@/store/index.js";
import { useUserStore } from "@/store/store.js";
const useUser = useUserStore(pinia);
const props = defineProps({
// 可配置的水印内容
text: {
type: Array,
default: () => {
return [];
}
},
fontSize: {
type: Number,
default: 18
},
gap: {
type: Number,
default: 70
}
});
const bg = useWatermarkBg(props);
const parentRef = ref(null);
const flag = ref(0); // 声明一个依赖
let div;
watchEffect(() => {
flag.value; // 将依赖放在 watchEffect 里
if (!parentRef.value) {
return;
}
if (div) {
div.remove();
}
const { base64, styleSize } = bg.value;
useUser.setWaterMark(base64);
div = document.createElement("div");
div.style.backgroundImage = `url(${base64})`;
div.style.backgroundSize = `${styleSize}px ${styleSize}px`;
div.style.backgroundRepeat = "repeat";
div.style.zIndex = 9999;
div.style.position = "absolute";
div.style.inset = 0;
div.style.pointerEvents = "none";
parentRef.value.appendChild(div);
});
let ob;
// 防篡改重要步骤
onMounted(() => {
// 监听浏览器dom操做变化
ob = new MutationObserver(records => {
for (const record of records) {
for (const dom of record.removedNodes) {
if (dom === div) {
flag.value++; // 删除节点的时候更新依赖
return;
}
}
if (record.target === div) {
flag.value++; // 修改属性的时候更新依赖
return;
}
}
});
// 启用监听
ob.observe(parentRef.value, {
childList: true,
attributes: true,
subtree: true
});
});
// 销毁监听
onUnmounted(() => {
ob && ob.disconnect();
div = null;
});
</script>
<style lang="less" scoped>
.watermark-container {
width: 100%;
height: 100%;
}
</style>