vue3+ts实现给图片增加水印-自定义指令

13 篇文章 1 订阅
7 篇文章 0 订阅

效果:
在这里插入图片描述
文件目录结构:
在这里插入图片描述
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);
}

src/directives/waterMark.ts

// 全局保存 canvas 和 div ,避免重复创建(单例模式)
const globalCanvas = null;
const globalWaterMark = null;

// watermark 样式
let style = `
display: block;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-repeat: repeat;
pointer-events: none;`;

const getDataUrl = ({
  font = "16px normal",
  fillStyle = "rgba(180, 180, 180, 0.3)",
  text = "请勿外传",
}) => {
  const rotate = -20;
  const canvas = globalCanvas || document.createElement("canvas");
  const ctx = canvas.getContext("2d"); // 获取画布上下文

  if (ctx) {
    ctx.rotate((rotate * Math.PI) / 180);
    ctx.font = font;
    ctx.fillStyle = fillStyle;
    ctx.textAlign = "left";
    ctx.textBaseline = "middle";
    ctx.fillText(text, canvas.width / 3, canvas.height / 2);
  }

  return canvas.toDataURL("image/png");
};

const setWaterMark = (el: HTMLElement, binding: any = {}) => {
  const { parentElement } = el;

  // 获取对应的 canvas 画布相关的 base64 url
  const url = getDataUrl({ ...binding, text: binding });

  // 创建 waterMark 父元素
  const waterMark = globalWaterMark || document.createElement("div");
  waterMark.className = `water-mark`; // 方便自定义展示结果
  style = `${style}background-image: url(${url});`;
  waterMark.setAttribute("style", style);

  if (parentElement) {
    // 将对应图片的父容器作为定位元素
    parentElement.setAttribute("style", "position: relative;");

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

// 监听 DOM 变化
const createObserver = (el: HTMLElement, binding: any) => {
  let waterMarkEl: any = null;
  if (el.parentElement) {
    waterMarkEl = el.parentElement.querySelector(".water-mark");
  }

  const observer = new MutationObserver((mutationsList) => {
    if (mutationsList.length) {
      const { removedNodes, type, target } = mutationsList[0];
      const currStyle = waterMarkEl.getAttribute("style");

      // 证明被删除了
      if (removedNodes[0] === waterMarkEl) {
        observer.disconnect();
        init(el, binding);
      } else if (
        type === "attributes" &&
        target === waterMarkEl &&
        currStyle !== style
      ) {
        waterMarkEl.setAttribute("style", style);
      }
    }
  });
  if (el.parentElement) {
    observer.observe(el.parentElement, {
      childList: true,
      attributes: true,
      subtree: true,
    });
  }
};

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

// 定义指令配置项
const directives: any = {
  mounted(el: HTMLElement, binding: any) {
    el.onload = init.bind(null, el, binding);
  },
};

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

src/main.ts

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import directives from "./directives";
createApp(App).use(store).use(router).use(directives).mount("#app");

使用

 <img alt="Vue logo" v-watermark="'水印'" src="../assets/logo.png" />

App.vue中水印样式

.water-mark {
  display: inline-block;
  overflow: hidden;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  background-repeat: repeat;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值