vue实现水印功能

目录

一、应用场景

二、实现原理

三、详细开发

1.水印的实现方式

2.防止用户通过控制台修改样式去除水印效果(可跳过,有弊端)

3.水印的使用

(1)单页面/全局使用

(2)全局使用+个别页面去掉

四、总结


一、应用场景

在网页中添加水印的作用可以有多个方面,一个重要的作用就是版权保护 ,防止未经授权就复制截图或使用,在文档中可以帮助标识文档的来源,审查追踪 等,也可以展示企业信息 ,或者作为提示信息 告诉用户当前页面谨慎处理,也能在敏感信息 的页面提示用户保护信息安全等。注意:制作页面中的水印要平衡用户体验和需求,确保水印不要太大,太密,太突兀,干扰页面浏览和操作


二、实现原理

在页面里添加水印,一种是特定页面加水印,那么本页面加水印功能即可,用CSS/JavaScript都可以实现,另一种是全局都加水印,这种可以考虑某些页面不需要加水印,在路由守卫或者其他地方去掉即可。

写在前面,水印的实现原理创建一个canvas,画一个客户端高x客户端宽的画布,里面画满水印,并将其的层级z-index设置最高,使其一直显示在界面的最上方。

水印的内容,可以根据应用的场景而变换,比如版权保护,这些最好显示版权的归属方之类的,有些出于标识来源加的水印,则需要考虑当前用户的信息,比如用户名等等。

实现效果如下:


三、详细开发

首先谈一下水印的实现方式,再说怎么加水印。

1.水印的实现方式

我们可以在utils下新建一个文件:watermark.js,代码如下:

javascript 复制代码
let watermark = {}
let idd = "1.23452384164.123412416"
let setWatermark = (str, srt1, srt2, srt3) => {
  let id = idd
  if (document.getElementById(id) !== null) {
    document.body.removeChild(document.getElementById(id))
  }

  //创建一个画布
  let can = document.createElement("canvas")
  //设置画布的长宽
  can.width = 600
  can.height = 450                                                                                   

  let cans = can.getContext("2d")
  //旋转角度
  cans.rotate((-15 * Math.PI) / 180)
  cans.font = "18px Vedana"
  //设置填充绘画的颜色、渐变或者模式
  cans.fillStyle = "rgba(200, 200, 200, 0.40)"
  //设置文本内容的当前对齐方式
  cans.textAlign = "left"
  //设置在绘制文本时使用的当前文本基线
  cans.textBaseline = "Middle"
  //在画布上绘制填色的文本(输出的文本,开始绘制文本的X坐标位置,开始绘制文本的Y坐标位置)
  cans.fillText(str + srt1, can.width / 8, can.height / 2)
  cans.fillText(srt2, can.width / 8, can.height / 2.3)
  cans.fillText(srt3, can.width / 8, can.height / 2.7)
  let div = document.createElement("div")
  div.id = id
  const styleStr = `
            position:fixed;
            visibility:visible !important;
            top:30px;
            width:${document.documentElement.clientWidth}px;
            height:${document.documentElement.clientHeight}px;
            left:0;
            z-index:100000;
            pointer-events:none;
            background:url('${can.toDataURL("image/png")}') left top repeat`
  div.setAttribute("style", styleStr)
  // div.style.width = document.documentElement.clientWidth + 'px';
  // div.style.height = document.documentElement.clientHeight + 'px';
  document.body.appendChild(div)
  //此方法是防止用户通过控制台修改样式去除水印效果
  /* MutationObserver 是一个可以监听DOM结构变化的接口。 */
  // const observer = new MutationObserver(() => {
  //   const wmInstance = document.getElementById(id)
  //   if (
  //     (wmInstance && wmInstance.getAttribute("style") !== styleStr) ||
  //     !wmInstance
  //   ) {
  //     //如果标签在,只修改了属性,重新赋值属性
  //     // console.log("水印属性修改了")
  //     if (wmInstance) {
  //       // 避免一直触发
  //       observer.disconnect();
  //       console.log("水印属性修改了")
  //       wmInstance.setAttribute("style", styleStr)
  //     } else {
  //       /* 此处根据用户登录状态,判断是否终止监听,避免用户退出后登录页面仍然有水印 */
  //       if (store.state.user.token) {
  //         //标签被移除,重新添加标签
  //         // console.log('水印标签被移除了');
  //         document.body.appendChild(div)
  //       } else {
  //         observer.disconnect()
  //       }
  //     }
  //   }
  // })
  // observer.observe(document.body, {
  //   attributes: true,
  //   subtree: true,
  //   childList: true,
  // })
  return id;
}

// 该方法只允许调用一次
watermark.set = (str, srt1, srt2, srt3) => {
  let id = setWatermark(str, srt1, srt2, srt3)
  setInterval(() => {
    if (document.getElementById(id) === null) {
      id = setWatermark(str, srt1, srt2, srt3)
    }
  }, 500)
  window.onresize = () => {
    setWatermark(str, srt1, srt2, srt3)
  }
}
// 移除
const outWatermark = id => {
  if (document.getElementById(id) !== null) {
    const div = document.getElementById(id)
    div.style.display = "none"
  }
}

watermark.remove = () => {
  const str = idd
  outWatermark(str)
}

// 将 watermark 的控制方法挂载到 window 对象上
window.watermark = watermark

export default watermark

上面的代码,很多人都写过,这里实现的也是大致效果,原理简单来讲就是:创建一个canvas,画一个客户端高x客户端宽的画布,里面画满水印,并将其的层级z-index设置最高,使其一直显示在界面的最上方,水印的效果就根据业务需求来调整。

这里将水印的控制方法(set和remove)都挂载在了window上,那么不论在哪个页面使用都可以直接调window来操作水印,水印的传参设置了四个str,其实可以根据实际情况添加更多,定制各样的效果。

2.防止用户通过控制台修改样式去除水印效果(可跳过,有弊端)

这里就是指上面代码里注释的的功能,可根据需求添加。

这个功能的原理:

  1. 创建了一个 MutationObserver 实例,(MutationObserver 允许开发人员监视 DOM 树的变化,并在发生变化时执行相应的操作),通过传一个制定ID的元素,将其存储在 wmInstance 变量中。
  2. 然后检查wmInstance 是否存在,及其style属性是否与指定的styleStr 变量相匹配,来判断水印是否需要更新。
  3. 如果 wmInstance 存在且其 style 属性不匹配,或者 wmInstance 不存在,则进行相应的处理:
    (1)如果 wmInstance 存在,则更新其 style 属性为 styleStr
    (2)如果 wmInstance 不存在,则检查用户登录状态。如果用户已登录(假设通过 store.state.user.token 判断),则向页面添加新的水印元素(假设该元素已在其他地方定义)。否则,断开 observer 的监听。
  4. 最后,调用 observer.observe() 方法开始观察文档主体的变化。选项对象指定了要观察的变化类型,包括 attributessubtreechildList

MutationObserver是什么?

MutationObserver 是 Web API 中的一部分,用于监视 DOM(文档对象模型)树的变化。它允许开发人员异步地观察文档中的节点并对其进行相应的处理。

在 Web 开发中,DOM 是指用于表示文档结构的树形数据结构,它由节点(node)组成,每个节点代表文档中的不同部分,如元素、属性、文本等。DOM 的结构和内容可能在页面加载后发生变化,比如用户的交互行为、脚本操作等都可能导致 DOM 发生变化。

MutationObserver 提供了一种机制,让开发人员可以监视 DOM 树的这些变化,并在变化发生时执行回调函数。这使得开发人员可以更灵活地响应 DOM 变化,而不必通过定时器或事件监听器等方式来轮询检查 DOM。

使用 MutationObserver,开发人员可以监视以下类型的 DOM 变化:

  1. 属性的改变(例如,元素的属性值发生变化)。
  2. 节点的添加或删除(例如,元素被插入或从 DOM 中移除)。
  3. 子节点的改变(例如,元素的子节点被添加或移除)。

通过 MutationObserver,开发人员可以更有效地监视 DOM 变化,从而实现更灵活、高效的 DOM 操作和交互。这在诸如单页面应用(SPA)等需要动态更新页面内容的场景中特别有用。

(上述查自网络)

这个功能的弊端是, 如下图所示,如果浏览器修改窗口大小,也会触发水印的修改,并且水印的覆盖会带来一定视觉上的"卡顿",实际使用中可能卡顿不是那么明显,但是这种情况也是值得考虑的。

3.水印的使用

使用的方式,有两种,局部使用和全局使用,就类似于我们引入UI组件库的组件一样,封装的水印js也需要局部或者全局注册。

(1)单页面/全局使用

这里就比较简单,我们在需要加水印的页面,引入水印,然后可以在mounted生命周期里调用它就行了。

javascript 复制代码
import Watermark from "@/utils/watermark"


mounted() {
    if (!Watermark) {
      Watermark = null
      // console.log("无水印",Watermark)
      return
    } else {
      Watermark.set(
        '第一行','第二行','第三行','第二行'
      )
    }
  },

如果是 全局使用,就在app.vue的页面里,根据当前页面的路径或其他标识来判断是否需要添加水印。

(2)全局使用+个别页面去掉

这个有多种实现方式,需要考虑业务场景,我这里推荐借助路由守卫,在router的路由守卫拦截的时候进行水印的set或者remove操作,如下:

javascript 复制代码
router.afterEach((to) => {
  
  let Watermark= window.watermark
  if(!Watermark ){
    Watermark=null
    // console.log(store.state.app.watermark,"store.state.app.watermark");
    return
  }
  if (
    to.fullPath === "/login" || to.fullPath === "/test"
  ) {
    Watermark.remove()
  } else {
    Watermark.set(
      '第一行','第二行','第三行','第二行'
    )
  }
})

使用路由守卫进行拦截的优点是:

路由守卫可以针对每个路由进行拦截,并判断是否需要添加水印,如果在特定路由不需要添加水印,可以在路由拦截时不调用水印脚本(或者remove),对水印添加的控制更加精细。


四、总结

在水印的实现里,第二种情况,我推荐结合两者的方法可以更好地满足不同场景下的需求,即在 app.vue中判断大部分页面是否需要添加水印,然后在路由守卫中针对个别页面进行额外的控制,这样页面就能满足大部分场景的要求。

相关推荐
练习两年半的工程师1 小时前
使用React和google gemini api 打造一个google gemini应用
javascript·人工智能·react.js
勘察加熊人2 小时前
angular九宫格ui
javascript·ui·angular.js
姑苏洛言3 小时前
30天搭建消防安全培训小程序
前端
左钦杨4 小时前
Nuxt2 vue 给特定的页面 body 设置 background 不影响其他页面
前端·javascript·vue.js
yechaoa4 小时前
【揭秘大厂】技术专项落地全流程
android·前端·后端
MurphyChen4 小时前
🤯 一行代码,优雅的终结 React Context 嵌套地狱!
前端·react.js
逛逛GitHub4 小时前
推荐 10 个受欢迎的 OCR 开源项目
前端·后端·github
_xaboy5 小时前
开源 FormCreate 表单设计器配置组件的多语言
前端·vue.js·低代码·开源·可视化表单设计器
uglyduckling04125 小时前
小程序构建NPM失败
前端·小程序·npm
草原上唱山歌5 小时前
C/C++都有哪些开源的Web框架?
前端·c++·开源