基于Vue3+Ts实现水印方法封装

基于Vue3+Ts实现水印方法封装

昨天在看公司源码的时候看到一个 utils 文件夹里封装了一个 waterMark.js,是实现添加水印功能的,研究了一下,是通过 canvas 画布画出水印然后再转换成图片最后再作为页面元素的背景,并且里面会返回一个唯一的标识符 id 来防止用户篡改,在项目中的应用具体是通过 sessionStorage.getItem() 来获取用户名然后再在后面添加一段当前的时间。

公司项目的水印功能是通过 Vue2 实现的,而我觉得这个水印功能具体还能继续优化一下,因为刚学 Ts 不久,我就想着用 Vue3 + Ts 尝试着重新封装一下,刚好在掘金上看到有一位博主写的:vue3 项目添加水印的实现方法 - 掘金 (juejin.cn) ,是通过 Vue3 + Ts 封装实现的,二者都是同样的思路,所以我就在这位博主代码的基础上进一步做了封装优化。

具体优化

  1. 对于水印的添加,可以选择在水印内容下方添加水印的生成时间,具体如图所示:
  1. 实现两种添加水印的方式,一种为全屏添加,一种为指定容器添加,会根据传入的参数进行判断:
  1. 原来添加水印的容器会被监听,当容器大小变化时会重新调用设置水印方法实现水印的自适应,在此基础上加了:当组件销毁时,移除所有事件的监听。

代码实现

因为要实现全屏和局部容器添加水印两种方式,而局部容器无法通过 onresize 方法实现监听,在这里就统一采用 new ResizeObserver() 这个 API 来实现元素大小的监听,具体使用方法可以这篇文章:监测DOM元素尺寸大小变化|Vue - 掘金 (juejin.cn)

typescript 复制代码
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参数是一个可选的参数,用于指定水印的容器,默认为整个页面。如果提供了容器,水印将被添加到指定容器中。如果未提供容器,水印将添加到整个页面中。 - 组件销毁时会自动清除监听,无需手动清除。

实际应用例子如下:

vue 复制代码
<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源码地址

相关推荐
莹雨潇潇7 分钟前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr15 分钟前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho1 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
小白学习日记3 小时前
【复习】HTML常用标签<table>
前端·html
程序员大金3 小时前
基于SpringBoot+Vue+MySQL的装修公司管理系统
vue.js·spring boot·mysql
丁总学Java3 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele3 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
懒羊羊大王呀3 小时前
CSS——属性值计算
前端·css