vue实现水印功能

news2024/9/29 17:29:21

目录

一、应用场景

二、实现原理

三、详细开发

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1485503.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL 常用优化方式

MySQL 常用优化方式 sql 书写顺序与执行顺序SQL设计优化使用索引避免索引失效分析慢查询合理使用子查询和临时表列相关使用 日常SQL优化场景limit语句隐式类型转换嵌套子查询混合排序查询重写 sql 书写顺序与执行顺序 (7) SELECT (8) DISTINCT <select_list> (1) FROM &…

18.题目:编号760 数的计算

题目&#xff1a; ###该题主要考察递推、递归 将该题看成若干个子问题 #include<bits/stdc.h> using namespace std; const int N20; int a[N];int dfs(int dep){int res1;for(int i1;i<a[dep-1]/2;i){a[dep]i;resdfs(dep1);}return res; }int main(){int n;cin>…

论文阅读_代码生成模型_CodeLlama

英文名称: Code Llama: Open Foundation Models for Code 中文名称: Code Llama&#xff1a;开放基础代码模型 链接: https://arxiv.org/abs/2308.12950 代码: https://github.com/facebookresearch/codellama 作者: Baptiste Rozire, Jonas Gehring, Fabian Gloeckle, Sten So…

React的fiber原理

在读完这篇文章之后&#xff0c;大家可以回到文章开头再捋一下以下几个关键词&#xff0c;将React的 Fiber架构原理彻底搞清楚。 关键词&#xff1a; requestIdleCallback、IdleDeadlineFiber&#xff1a;React的一个执行单元 在Fiber 出现之前&#xff0c;React存在什么问题…

bxCAN总线Loopback功能实现(STM32F4xx)

目录 概述 1 认识bxCAN Loopback 1.1 环回模式 1.2 环回模式特点 2 使用STM32CubeMX 生成工程 2.1 参数配置 2.2 生成工程代码 2.4 了解can.c 3 认识Hal库中的bxCAN 3.1 认识3个重要数据结构 3.2 函数组 3.2.1 初始化函数组 3.2.2 控制函数组 3.2.3 中断管理函数…

【风格迁移】pix2pixHD:高分辨率图像生成

pix2pixHD&#xff1a;高分辨率图像生成 提出背景问题1: 如何提高生成图像的照片级真实感和分辨率&#xff1f;问题2: 如何利用实例级别的对象语义信息进一步提高图像质量&#xff1f;问题3: 如何实现图像合成的多模态性&#xff0c;允许交互式对象编辑&#xff1f; pix2pixHD …

【STK】手把手教你利用STK进行仿真-STK软件基础01 STK的对象组织模式

STK系统采用面向对象和分级组织结构的管理模式对仿真对象进行管理&#xff0c;这种组织二行管理模式非常符合人类的认知习惯&#xff0c;易于理解。STK最基本的对象为场景&#xff08;Scenario&#xff09;&#xff0c;在场景中可以面向对象和分级组织模式对各场景中的仿真对象…

[c++] c++ 中的顺序(构造,析构,初始化列表,继承)

对象构造的时候&#xff0c;对象成员变量的初始化顺序是什么样的 &#xff1f; 派生类构造的时候&#xff0c;先构造基类还是先构造派生类 &#xff1f; 构造函数中的初始化列表&#xff0c;初始化的顺序是列表的顺序吗 &#xff1f; 析构的时候&#xff0c;析构的顺序是什么…

华为数通方向HCIP-DataCom H12-821题库(多选题:41-60)

第41题 BGP OPEN消息中携带如下哪些信息? A、路由属性 B、BGP Router ID C、Hold time D、本地自治系统(AS)号 【参考答案】BCD 【答案解析】 B. BGP Router ID:OPEN消息中包含发送方BGP路由器的Router ID,用于唯一标识BGP路由器。C.Hold time:OPEN消息中包含发送方BGP路由…

区块链媒体发布推广10个热门案例解析-华媒舍

区块链技术的发展已经引起了媒体的广泛关注&#xff0c;越来越多的区块链媒体纷纷发布推广相关的热门案例。本文将介绍10个成功的区块链媒体推广案例&#xff0c;并分享它们的成功秘诀&#xff0c;帮助读者更好地了解区块链媒体推广的方法与技巧。 随着区块链技术的成熟和应用场…

vue2本地开发环境正常,生产环境下this.$router.push({ name: ‘login‘ })不跳转

如果在Vue.js 2中在本地开发环境下正常运行,但在生产环境下使用​​this.$router.push({ name: login })​​不起作用,可能有几个原因需要检查和解决: 路由配置问题: 确保你的路由配置正确,特别是确保在生产环境中,路由的配置和本地开发环境一致。检查是否正确设置了name…

课程表系列(BFS)

广度优先搜索 文章目录 广度优先搜索207. 课程表210. 课程表 II思路 630. 课程表 III1462. 课程表 IV547. 省份数量 207. 课程表 207. 课程表 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程…

多元函数积分思路合集

[曲线积分笔记]第一类曲线积分 [微积分笔记]第二类曲线/面积分总结 积分方法意义/提示平面第二型曲线积分化为定积分 ∮ L → ∫ a b \oint_L \to \int_a^b ∮L​→∫ab​平面第二型曲线积分格林公式 ∮ L → ∬ D \oint_L \to \iint_D ∮L​→∬D​化为对平面区域的二重积分空…

百度搜索引擎SEO优化方法

随着互联网的不断发展&#xff0c;搜索引擎已经成为人们获取信息、产品和服务的主要途径之一。而在中国&#xff0c;百度作为最大的搜索引擎&#xff0c;其影响力不可忽视。了解并掌握百度SEO关键词优化方法&#xff0c;对于提升网站在搜索引擎中的排名至关重要。 关键词选择&a…

chatgpt-3的文章生成器有哪些?可以批量生成文章的生成器

GPT-3&#xff08;Generative Pre-trained Transformer 3&#xff09;作为人工智能领域的一项重大突破&#xff0c;开启了新一代的文本生成技术。同时市面上也涌现出了一些GPT-3文章生成器&#xff0c;为用户提供了快速、高效地生成各种类型文章的工具。本文将介绍一些中国的GP…

JS:原型与原型链(附带图解与代码)

一、原型 写在前面&#xff1a; 任何对象都有原型。 函数也是对象&#xff0c;所以函数也有原型。 1.什么是原型 在 JavaScript 中&#xff0c;对象有一个特殊的隐藏属性 [[Prototype]]&#xff0c;它要么为 null&#xff0c;要么就是对另一个对象的引用&#xff0c;该对象…

MATLAB基于隐马尔可夫模型-高斯混合模型-期望最大化的MR图像分割

隐马尔可夫模型是一种统计模型&#xff0c;它描述了马尔可夫过程&#xff0c;隐马尔可夫过程中包含隐变量&#xff0c;语音识别和词性自动标注等一些领域常常使用隐马尔可夫模型方法来处理。马尔可夫过程是一类随机过程&#xff0c;马尔可夫链是它的原始模型&#xff0c;马尔可…

《OpenScene: 3D Scene Understanding with Open Vocabularies》阅读笔记1

传统的3D场景理解方法依赖于带标签的3D数据集,用于训练一个模型以进行单一任务的监督学习。我们提出了OpenScene,一种替代方法,其中模型在CLIP特征空间中预测与文本和图像像素共同嵌入的3D场景点的密集特征。这种零样本方法实现了与任务无关的训练和开放词汇查询。例如,为了…

03-JNI 类型和数据结构

上一篇&#xff1a; 02-设计概述 本章讨论 JNI 如何将 Java 类型映射为本地 C 类型。 3.1 原始类型 下表描述了 Java 原始类型及其与机器相关的本地等价类型。 为方便起见&#xff0c;定义如下&#xff1a; #define JNI_FALSE 0 #define JNI_TRUE 1 jsize 整数类型用于描述…

《Spring Security 简易速速上手小册》第8章 常见问题与解决方案(2024 最新版)

文章目录 8.1 异常处理和日志记录8.1.1 基础知识详解8.1.2 重点案例&#xff1a;统一异常处理案例 Demo拓展 8.1.3 拓展案例 1&#xff1a;日志记录策略案例 Demo拓展 8.1.4 拓展案例 2&#xff1a;日志聚合案例 Demo拓展 8.2 多租户安全性问题8.2.1 基础知识详解8.2.2 重点案例…