首页 前端知识 vue实现水印功能

vue实现水印功能

2024-04-29 11:04:54 前端知识 前端哥 716 753 我要收藏

目录

一、应用场景

二、实现原理

三、详细开发

1.水印的实现方式

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

3.水印的使用

(1)单页面/全局使用

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

四、总结


一、应用场景

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


二、实现原理

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

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

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

实现效果如下:


三、详细开发

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

1.水印的实现方式

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

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生命周期里调用它就行了。

import Watermark from "@/utils/watermark"


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

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

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

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

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中判断大部分页面是否需要添加水印,然后在路由守卫中针对个别页面进行额外的控制,这样页面就能满足大部分场景的要求。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/6251.html
标签
评论
发布的文章

JQuery中的load()、$

2024-05-10 08:05:15

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!