基于Vue3+Ts实现水印方法封装
昨天在看公司源码的时候看到一个 utils 文件夹里封装了一个 waterMark.js
,是实现添加水印功能的,研究了一下,是通过 canvas
画布画出水印然后再转换成图片最后再作为页面元素的背景,并且里面会返回一个唯一的标识符 id 来防止用户篡改,在项目中的应用具体是通过 sessionStorage.getItem()
来获取用户名然后再在后面添加一段当前的时间。
公司项目的水印功能是通过 Vue2 实现的,而我觉得这个水印功能具体还能继续优化一下,因为刚学 Ts 不久,我就想着用 Vue3 + Ts 尝试着重新封装一下,刚好在掘金上看到有一位博主写的:vue3 项目添加水印的实现方法 – 掘金 (juejin.cn) ,是通过 Vue3 + Ts 封装实现的,二者都是同样的思路,所以我就在这位博主代码的基础上进一步做了封装优化。
具体优化
- 对于水印的添加,可以选择在水印内容下方添加水印的生成时间,具体如图所示:
- 实现两种添加水印的方式,一种为全屏添加,一种为指定容器添加,会根据传入的参数进行判断:
- 原来添加水印的容器会被监听,当容器大小变化时会重新调用设置水印方法实现水印的自适应,在此基础上加了:当组件销毁时,移除所有事件的监听。
代码实现
因为要实现全屏和局部容器添加水印两种方式,而局部容器无法通过 onresize
方法实现监听,在这里就统一采用 new ResizeObserver()
这个 API 来实现元素大小的监听,具体使用方法可以这篇文章:监测DOM元素尺寸大小变化|Vue – 掘金 (juejin.cn)
import { onBeforeUnmount } from "vue";
export const getMark = () => {
/**
* 设置水印方法
* @param {string} str - 输出文本
* @param {HTMLElement} [container] - 设置水印的容器(如果不写则默认设置全屏水印)
* @param {boolean} [createTime] - 水印生成时间
*/
const setWaterMark = (str: string, createTime: boolean, container?: HTMLElement) => {
// 创建唯一标识符id
const id = "1.23452384164.123412416";
if (document.getElementById(id) !== null) {
container ? container.removeChild(document.getElementById(id)!) : document.body.removeChild(document.getElementById(id)!);
}
// 创建画布
const can = document.createElement("canvas");
// 设置画布长宽
can.width = 150;
can.height = 120;
// 获取画布元素2D渲染上下文
const cans = can.getContext("2d")!;
// 设置旋转角度(逆时针旋转15度)
cans.rotate((-15 * Math.PI) / 180);
// 设置字体
cans.font = "18px Vedana";
// 设置填充绘色
cans.fillStyle = "rgba(200, 200, 200, 0.40)";
// 设置在绘制文本时使用当前文本基线
cans.textBaseline = "middle";
/**
* 在画布上绘制填色文本
* @param {string} str - 输出文本
* @param {number} width - 开始绘制文本X坐标位置
* @param {number} height - 开始绘制文本Y坐标位置
*/
cans.fillText(str, can.width / 8, can.height / 2);
if (createTime) {
// 获取当前时间并格式化
let curDate = new Date();
let dateStr = curDate.toLocaleDateString();
let timeStr = curDate.toLocaleTimeString();
let timeStamp = `${dateStr} ${timeStr}`;
const timeTextWidth = cans.measureText(timeStamp).width;
const timeTextX = (can.width - timeTextWidth) / 2;
cans.font = "14px Vedana";
cans.fillText(timeStamp, timeTextX, (can.height / 3) * 2);
}
// 创建全屏浮动全屏浮动div, 将画布作为背景
const div = document.createElement("div");
div.id = id;
div.style.pointerEvents = "none";
div.style.top = "20px";
div.style.left = "0px";
div.style.position = "absolute";
div.style.zIndex = "999999";
div.style.width = container ? container.clientWidth + "px" : "100%";
div.style.height = container ? container.clientHeight + "px" : "100%";
div.style.background =
"url(" + can.toDataURL("image/png") + ") left top repeat";
if (container) {
container.style.position = "relative";
container.appendChild(div);
} else {
document.body.appendChild(div);
}
return id;
};
// 只允许调用一次该方法
const waterMark = (str: string, createTime: boolean, container?: HTMLElement) => {
let id = setWaterMark(str, createTime, container);
// 定时检查是否存在具有相同标识符的元素
setInterval(() => {
if (document.getElementById(id) === null) {
id = setWaterMark(str, createTime, container);
}
}, 500);
// 监听窗口大小调整, 确保水印的适应性
const handleResize = () => {
setWaterMark(str, createTime, container);
};
const observer = new ResizeObserver(handleResize);
// 全屏监听
if (!container) {
observer.observe(document.documentElement, { box: "content-box" });
}
// 指定容器监听
else {
observer.observe(container, { box: "content-box" });
}
// 组件销毁时清除监听
onBeforeUnmount(() => {
observer.disconnect();
});
};
return { waterMark }
};
具体应用
在组件中直接引入 waterMark.ts
解构出相应的方法进行使用即可,其中 waterMark()
方法的参数情况如下表:
方法名 | waterMark |
---|---|
参数 | str: string – 水印文本 createTime: boolean – 是否添加水印生成时间 container?: HTMLElement – 水印容器(可选,默认为整个页面) |
返回值 | 无 |
功能 | 创建水印,并根据需要定时检查是否存在具有相同标识符的元素。监听窗口大小调整,以确保水印的适应性,并在组件销毁时清除监听。 |
注意事项 | – str 参数为水印文本,可以是任意字符串。 – createTime 参数用于控制是否添加水印生成时间。 – container 参数是一个可选的参数,用于指定水印的容器,默认为整个页面。如果提供了容器,水印将被添加到指定容器中。如果未提供容器,水印将添加到整个页面中。 – 组件销毁时会自动清除监听,无需手动清除。 |
实际应用例子如下:
<template>
<div class="box" ref="boxRef"></div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { getMark } from "../utils/waterMark";
const boxRef = ref(null);
const { waterMark } = getMark();
onMounted(() => {
waterMark("PandaGuo", false, boxRef.value!); //添加水印
});
</script>
<style scoped>
.box {
width: 500px;
height: 500px;
background-color: black;
}
</style>
源码地址
[github.com/Panda-Gu0/W…] github源码地址
原文链接:https://juejin.cn/post/7262279674594836540 作者:迪士尼在逃保安