自定义指令-水印实现

在项目中,我们可能会遇到添加水印效果的需求,下面将会介绍一种基本的实现方法。

一、代码实现


watermark.vue 文件

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

const getDataUrl = (binding: any) => {
  const rotate = -20;
  const canvas = document.createElement("canvas");
  // canvas.style.width = "500";
  // canvas.style.height = "500";
  const ctx = canvas.getContext("2d");

  ctx?.rotate((rotate * Math.PI) / 180);
  ctx.font = binding.font;
  ctx.fillStyle = binding.fillStyle;
  ctx?.fillText(binding.text || "机密文件", -10, 108);

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

const WaterMarker = {
  mounted(el: HTMLElement, binding: any) {
    init(el, binding);
  },
};

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

// 设置水印
const setWaterMark = (el: HTMLElement, binding: any = {}) => {
  const url = getDataUrl(binding);

  // 创建 waterMark
  const waterMark = document.createElement("div");
  waterMark.className = `water-mark`;
  style = `${style}background-image: url(${url});`;
  waterMark.setAttribute("style", style);
  el.setAttribute("style", "position: relative;");
  el.appendChild(waterMark);
};

const createObserver = (el: HTMLElement, binding: any) => {
  const waterMarkEl = el.querySelector(".water-mark");
  // Firefox和Chrome早期版本中带有前缀
  const MutationObserver =
    window.MutationObserver ||
    window?.WebKitMutationObserver ||
    window?.MozMutationObserver;
  // 监听节点变化
  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) {
        // 停止观察
        observer.disconnect();

        init(el, binding);
      } else if (
        type === "attributes" &&
        target === waterMarkEl &&
        currStyle !== style
      ) {
        waterMarkEl.setAttribute("style", style);
      }
    }
  });

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

export default WaterMarker;



在Directive的index.ts文件中引入水印(WaterMarker.vue)


import WaterMarker from "./WaterMarker";

interface IDirectives {
  [key: string]: any;
}
// 自定义指令
const directives: IDirectives = {
  waterMarker: WaterMarker,
};

export default {
  install(app: any) {
    Object.keys(directives).forEach((key) => {
      app.directive(key, directives[key]);
    });
  },
};

引入

在这里插入图片描述

实现效果

在这里插入图片描述

二、实现思路

2.1 水印全局自定义指令

在这里我们采用的是全局自定义指令的方式,这样方面我们在想要的页面直接加上相应的指令即可。

2.2 canvas实现逻辑

水印相当于是一张重复的背景图片印上我们想要加入的名字等,所以我们很容易想到利用canvas进行实现,在上面getDataUrl 方法则是其实现逻辑。

2.3 利用MutationObserver 监控

MutationObserver给开发者们提供了一种能在某个范围内的DOM树发生变化时作出适当反应的能力在这里,我们需要监听子节点,属性相应的变化。若改变了则重新渲染水印,从而防止用户行为而导致水印消失。

配置观察者对象行为的对象,该对象可以拥有下面这些属性:

属性描述
childList如果需要观察目标节点的子节点(新增了某个子节点,或者移除了某个子节点),则设置为true.
attributes如果需要观察目标节点的属性节点(新增或删除了某个属性,以及某个属性的属性值发生了变化),则设置为true.
subtree除了目标节点,如果还需要观察目标节点的所有后代节点(观察目标节点所包含的整棵DOM树上的上述三种节点变化),则设置为true.

MutationRecord对象会作为第一个参数传递给观察者对象包含的回调函数,该对象有下面这些属性

属性类型描述
typeString如果是属性发生变化,则返回attributes.如果是一个CharacterData节点发生变化,则返回characterData,如果是目标节点的某个子节点发生了变化,则返回childList.
targetNode返回此次变化影响到的节点,具体返回那种节点类型是根据type值的不同而不同的. 如果type为attributes,则返回发生变化的属性节点所在的元素节点,如果type值为characterData,则返回发生变化的这个characterData节点.如果type为childList,则返回发生变化的子节点的父节点.
removedNodesNodeList返回被删除的节点,或者为null.

以上只是列举了本次水印用到的一些属性,具体属性请参考文档http://web.h3399.cn/MutationObserver.htm

三、弊端

上面水印效果能满足大部分要求,但如果客户懂点浏览器操作,水印效果也容易轻松去除

在这里插入图片描述
如上图,将Disable JavaSccript 勾选,页面将不能执行JavaSccript代码,从而水印也会消失


若哪位大佬有更好的解决方案,麻烦点赞评论或私聊告诉一声,感谢啦
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值