基于Vue3水印组件封装:防篡改守护

admin6个月前笔记62

基于Vue 3的全新水印通用组件。这款组件不仅功能强大,而且易于集成,能够轻松为您的网页或应用添加自定义水印,有效防止内容被篡改或盗用。

一,编写watermark组件

<template>
   <div ref="watermarkContainerRef" class="watermark-container">
      <!--  插槽-->
     <slot></slot>
   </div></template><script setup>import { ref, onMounted, watchEffect, onUnmounted, computed } from "vue";// 使用 defineProps 定义一个组件的 props,这些 props 描述了组件从父组件接收的属性const props = defineProps({  // 文本内容,类型为字符串,必须提供,默认值为'张苹果博客'
  text: {    type: String,    required: true,    default: '张苹果博客'
  },  // 字体大小,类型为数字,默认值为10
  fontSize: {    type: Number,    default: 10,
  },  // 间距,类型为数字,默认值为1
  gap: {    type: Number,    default: 1,
  },  // 颜色,类型为字符串,默认值为'rgba(82,75,75,0.58)'
  color: {    type: String,    default: 'rgba(82,75,75,0.58)',
  }
});// 定义一个用于绘制水印的函数,这里可以封装一下单独引入。// 它是一个计算属性,意味着它的值会根据其依赖的 props 的变化而自动重新计算const waterMarkBg = (props) => {  return computed(() => {    // 创建一个新的 canvas 元素
    const canvas = document.createElement("canvas");    // 获取设备的像素比,如果未定义则默认为1
    const devicePixelRatio = window.devicePixelRatio || 1;    // 根据像素比计算字体大小
    const fontSize = props.fontSize * devicePixelRatio;    // 设置字体样式
    const font = fontSize + "px serif";    // 获取 canvas 的 2D 渲染上下文
    const ctx = canvas.getContext("2d");    // 设置字体
    ctx.font = font;    // 测量文本的宽度
    const { width } = ctx.measureText(props.text);    // 计算 canvas 的大小,至少为 60,并根据文本宽度和间距因子进行调整
    const canvasSize = Math.max(60, width) * props.gap + devicePixelRatio;    // 设置 canvas 的宽高
    canvas.width = canvasSize;
    canvas.height = canvasSize;    // 将 canvas 的原点移动到中心
    ctx.translate(canvas.width / 2, canvas.height / 2);    // 旋转 canvas 45 度
    ctx.rotate((Math.PI / 180) * -45);    // 设置填充颜色
    ctx.fillStyle = props.color;    // 设置文本对齐方式和基线
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";    // 再次设置字体
    ctx.font = font;    // 在 canvas 上填充文本
    ctx.fillText(props.text, 0, 0);    // 返回一个对象,包含 base64 编码的图片数据、canvas 的大小和样式尺寸
    return {      base64: canvas.toDataURL(),      size: canvasSize,      styleSize: canvasSize / devicePixelRatio
    };
  });
};// 用于存储 MutationObserver 的变量let ob;// 用于存储水印 div 的变量let div;// 调用 waterMarkBg 函数获取水印相关的计算属性const bg = waterMarkBg(props);// 创建一个 ref 用于存储水印容器的 DOM 引用const watermarkContainerRef = ref('');// 创建一个 ref 用于标记水印是否需要重新生成const flag = ref(0);// 在组件挂载后执行onMounted(() => {  // 创建一个新的 MutationObserver,用于监听水印容器的变化
  ob = new MutationObserver((records) => {    // 遍历所有的变化记录
    for (const record of records) {      // 遍历所有被移除的节点
      for (const dom of record.removedNodes) {        // 如果被移除的节点是水印 div,则更新 flag 的值并返回
        if (dom === div) {
          flag.value++;          return;
        }
      }      // 如果变化的节点就是水印 div,则更新 flag 的值并返回
      if (record.target === div) {
        flag.value++;        return;
      }
    }
  });  // 包括子节点的变化、属性的变化以及子树的变化
  ob.observe(watermarkContainerRef.value,{    childList:true,    attributes:true,    subtree:true
  });
})//卸载onUnmounted(()=>{
  ob && ob.disconnect();
  div=null;
})// 生成水印watchEffect(() => {  // 触发 watchEffect 的重新执行
  flag.value;  // 如果水印容器没有值,则直接返回,不执行后续操作
  if (!watermarkContainerRef.value) {    return;
  }  // 如果之前已经存在水印 div,则先移除它
  if (div) {
    div.remove();
  }  // 创建一个新的 div 元素用于作为水印的容器
  div = document.createElement('div');  // 从计算属性 bg 中获取 base64 编码的图片数据和样式尺寸
  const { base64, styleSize } = bg.value;  // 设置 div 的背景图片为水印图片的 base64 编码
  div.style.backgroundImage = `url(${base64})`;  // 设置背景图片的尺寸
  div.style.backgroundSize = `${styleSize}px ${styleSize}px`;  // 设置背景图片重复显示
  div.style.backgroundRepeat = "repeat";  // 设置水印 div 的 z-index 为 9999,以确保它显示在大多数其他元素之上
  div.style.zIndex = 9999;  // 设置水印 div 不响应鼠标事件,如点击、悬停等
  div.style.pointerEvents = "none";  // 设置水印 div 的位置为绝对定位
  div.style.position = "absolute";  // 使用 inset 属性设置 div 占据整个父容器的空间
  div.style.inset = "0";  // 将水印 div 添加到水印容器中
  watermarkContainerRef.value.appendChild(div);
});</script><style scoped>.watermark-container{  position: relative;
}</style>

二,在页面中引入使用

<template>
 <div>
   <n-grid>
     <n-gi style="margin: 15px"   span="6 1025:2  "  v-for="(item,index) in 4" :key="index">
<!--       引入 Watermark-->
         <Watermark :gap="gap" :text="text" :fontSize="fontSize" :color="color">
           <n-card
               v-motion-pop-visible-once
              title="标题"
               hoverable
           >
            这是内容 <br>
            这是内容 <br>
            这是内容 <br>
            这是内容 <br>
            这是内容 <br>
            这是内容 <br>
           </n-card>
         </Watermark>

     </n-gi>
   </n-grid>

 </div>
</template>

<script setup>
import Watermark from '../components/Watermark.vue'
import {ref} from "vue";
const text=ref('张苹果博客');
const gap=ref(1);
const fontSize=ref('10');
const color=ref('');
</script>

<style scoped>

</style>

三,效果图

image.png


相关文章

多主机网络下 Docker Swarm 模式的容器管理

多主机网络下 Docker Swarm 模式的容器管理

本文将以多主机网络环境为基础,探讨如何利用内置编排工具 Docker Swarm 模式对各主机上的容器加以管理。Docker Engine – Swarm 模式在多台主机之上运行 MySQL...

如何在Centos7配置ssh/rsh免密互信集群服务

如何在Centos7配置ssh/rsh免密互信集群服务

ssh免密互信操作一、在SSH服务器所在机器上1、以root用户登录,更改ssh配置文件 /etc/ssh/sshd_config,去除以下配置的注释RSAAuthentication ye...

凭什么说密钥比密码更好

凭什么说密钥比密码更好

密码已成为过去,密钥才是未来。也许人们已经有过这种感觉,或者已经想象过了。当意识到刚刚把登录证书输入了一个假网站的那一刻,你会感到恐慌。也许你马上就意识到了。又或者你在第二天返回后无法登录时才意识到这...

uni-app开发经验总结

uni-app开发经验总结

uni-app介绍及对其优缺点理解开发多端兼容应用的一种较好的方案:有时候一些关键的点上走不通,可能就需要花费很多额外的工作去做相应端的兼容,如百度小程序不支持scoped;App转为微信小程序的一点...

localhost、127.0.0.1和0.0.0.0和本机IP的区别

localhostlocalhost其实是域名,一般windows系统默认将localhost指向127.0.0.1,但是localhost并不等于127.0.0.1,localhost指向的IP地址...

香港虚拟银行开户攻略:一小时搞定ZA BANK、livi理慧银行、天星银行、蚂蚁银行

香港虚拟银行开户攻略:一小时搞定ZA BANK、livi理慧银行、天星银行、蚂蚁银行

10月中旬去了一趟香港,主要是为了激活香港招商永隆银行账户,顺便申请了几家虚拟银行:ZA BANK、livi理慧银行、天星银行、蚂蚁银行,全程手机APP操作,大约花了一个小时的时间。本文会介绍这几家虚...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。