vue3 watermark 添加防止删除水印

文章介绍了如何在Vite+Vue3+TS项目中创建一个防止删除的水印指令。通过定义Vue指令,利用canvas绘制水印并应用全局,同时使用MutationObserver监听DOM变化,实现在元素删除后重新生成水印,确保水印始终存在。
摘要由CSDN通过智能技术生成

vite+vue3+ts项目添加防止删除水印

以指令的方式为模块区域添加 水印

  1. 添加指水印指令
// ./src/directives/index.ts

import type { App } from 'vue';
import watermark from './waterMark';

export default function installDirective(app: App) {
  // 水印指令
  app.directive(watermark.name, watermark.directives);
}

  1. 指令实体代码
// ./src/directives/waterMark.ts
// 全局保存 canvas 和 div ,避免重复创建(单例模式)
const globalCanvas = document.createElement('canvas');
const globalWaterMark = document.createElement('div');

// 返回一个包含图片展示的 数据URL
const getDataUrl = (binding: any) => {
  const rotate = -20;
  const canvas = globalCanvas || document.createElement('canvas');
  const ctx = <CanvasRenderingContext2D>canvas.getContext('2d'); // 获取canvas画布的绘图环境
  canvas.width = 390; // 单个水印大小
  canvas.height = 180;
  ctx?.rotate((rotate * Math.PI) / 180); // 水印旋转角度
  ctx.font = binding.font || '12px Vedana';
  ctx.fillStyle = binding.fillStyle || 'rgba(200, 200, 200, 0.30)';
  ctx.textBaseline = 'middle';
  ctx?.fillText(binding.text || '默认水印文字', canvas.width / 10, canvas.height / 2);
  return canvas.toDataURL('image/png');
};

// watermark 样式
let style = `
display: block;
overflow: hidden;
position: absolute;
left: 0px;
top: 0px;
z-index: 100000;
font-size: 12px;
background-repeat: repeat;
background-position: center;
pointer-events: none;`;

// 定义指令配置项
const directives: any = {
  mounted(el: HTMLElement, binding: any) {
    // 注意img有onload的方法,如果自定义指令注册在html标签的话,只需要init(el, binding.value)
    // el.onload = init.bind(null, el, binding);
    init(el, binding.value);
  },
};

// 初始化
const init = (el: HTMLElement, binding: any = {}) => {
  // 设置水印
  setWaterMark(el, binding);
  // 启动监控
  createObserver(el, binding);
};

// 设置水印
const setWaterMark = (el: HTMLElement, binding: any) => {
  const { parentElement } = el;
  const { width, height } = parentElement!.getBoundingClientRect();
  // 获取对应的 canvas 画布相关的 base64 url
  const url = getDataUrl(binding);

  // 创建 waterMark 父元素
  const waterMark = globalWaterMark || document.createElement('div');
  waterMark.className = `pdp-water-mark`; // 方便自定义展示结果
  style = `${style}background-image: url(${url});width:${width}px; height:${height}px`;
  waterMark.setAttribute('style', style);
  // 如果父元素有自己的stayle 则获取后和自定义的拼接,并避免重复添加
  let currStyle = parentElement?.getAttribute('style') ? parentElement?.getAttribute('style') : '';
   currStyle = currStyle?.includes('position: relative')
    ? currStyle
    : currStyle + 'position: relative;';
  // 将对应图片的父容器作为定位元素
  parentElement?.setAttribute('style', currStyle);

  // 将图片元素移动到 waterMark 中
  parentElement?.appendChild(waterMark);
};

/**
 * 监听 DOM 变化
 * 用 MutationObserver 对水印元素进行监听,删除时,我们再立即生成一个水印元素就可以了
 * @param el
 * @param binding
 */
const createObserver = (el: any, binding: any) => {
  const waterMarkEl = el.parentElement?.querySelector('.pdp-water-mark');
  const observer = new MutationObserver((mutationsList) => {
    // console.log('mutationsList', mutationsList);
    if (mutationsList.length) {
      const { removedNodes, type, target } = mutationsList[0];
      const currStyle = waterMarkEl?.getAttribute('style');

      // 证明被删除了
      if (removedNodes[0] === waterMarkEl) {
        // 停止观察。调用该方法后,DOM 再发生变动,也不会触发观察器
        observer.disconnect();
        // 初始化(设置水印,启动监控)
        init(el, binding);
      } else if (type === 'attributes' && target === waterMarkEl && currStyle !== style) {
        waterMarkEl.setAttribute('style', style);
      }
    }
  });

  observer.observe(el.parentElement, {
    childList: true,
    attributes: true,
    subtree: true,
  });
};

export default {
  name: 'watermark',
  directives,
};

  1. 在项目入口文件引入指令并注册
import { createApp } from 'vue';
import { setupElementPlus } from '@/styles/index';
import { setupRouter } from './router';
import { setupStore } from './store/index';
import App from './App.vue';
import '@/styles/font/iconfont.css';
import permisson from './plugins/permission';
import XGantt from '@xpyjs/gantt';
import '@xpyjs/gantt/dist/index.css';
// 引入指令并注册使用
import directives from '@/directives';
async function bootstrap() {
  const app = createApp(App);

  setupStore(app);

  setupRouter(app);

  setupElementPlus(app);

  app.use(permisson).use(XGantt).use(directives);

  app.mount('#app');
}
bootstrap();

  1. 在需要加水印的dom上使用指令 v-watermark=“watermarkObj”
    <el-container class="container">
      <aside-menu />
      <el-main class="main">
         <header-menu />
        <el-container
          class="content"
          :style="{
            height: `${
              screenFullState ? '100%' : isShowHeadMenu ? 'calc(100% - 113px)' : 'calc(100% - 64px)'
            }`,
            overflowY: 'auto',
          }"
        >
          <router-view v-slot="{ Component }" v-watermark="watermarkObj">
            <transition name="fade-transform" mode="out-in">
              <component :is="Component" />
            </transition>
          </router-view>
        </el-container>
      </el-main>
    </el-container>
<script lang="ts" setup>
import { useStore as useBaseStore } from '../store/base';
const store = useStore();
	const wartermarkText = computed(() => {
		let info = store?.state?.user;
		return `${info.username}${info.phone}`;
	});
	let watermarkObj = reactive({
		text: wartermarkText.value,
	});
</script>
  1. 水印效果图
    水印效果图
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值