前端开发攻略---Vue实现防篡改水印的效果。删除元素无效!更改元素属性无效!支持图片、元素、视频等等。
1、演示
2、水印的目的
版权保护:水印可以在图片、文档或视频中嵌入作者、品牌或版权所有者的信息,以防止未经授权的复制、传播或使用。当其他人使用带有水印的内容时,可以追溯到原始作者或版权所有者,从而加强版权保护。
身份识别:水印可以用作作者或品牌的标识符,使观众能够轻松识别内容的来源。这对于在社交媒体上分享内容或在网络上发布作品的个人、摄影师、设计师或公司来说尤为重要。
品牌宣传:水印可以帮助提升品牌知名度和曝光度。通过在图片或视频中添加品牌标识,品牌可以在内容被分享或传播时获得额外的宣传效果。
内容跟踪:通过在内容中添加水印,可以跟踪内容的传播和使用情况。这对于了解内容在网络上的传播路径、受众和影响力等信息是有帮助的。
3、实话实说
如果通过我们技术层面要解决这种安全或者是版权之类的问题,只能说起一定的作用,增加那些不怀好意的人操作难度。
要说能够完全防止住,那是不可能的,一定是技术手段和非技术手段相结合,双管齐下。这样才能确保万无一失。
总之:防君子不防小人
4、API介绍(MutationObserver)
MutationObserver API 是 Web API 的一部分,用于监视 DOM 树的变化。它允许开发者注册一个回调函数,该函数在指定的 DOM 节点或子树发生变化时被调用。MutationObserver 是一个强大的工具,可以用于监视并响应 DOM 中的变化,而无需使用传统的事件监听器。
主要的组成部分包括:
MutationObserver 对象:用于观察 DOM 树的变化。通过创建 MutationObserver 的实例并传入一个回调函数,可以开始监视指定节点或节点集合的变化。
观察目标:要监视的 DOM 节点或节点集合。MutationObserver 可以观察单个节点、节点列表,甚至整个文档的变化。
回调函数:MutationObserver 注册的回调函数在观察的节点发生变化时被调用。回调函数接收一个 MutationRecord 对象数组作为参数,该数组包含描述每个变化的信息。
变化记录(MutationRecord):描述 DOM 变化的对象。每个 MutationRecord 包含有关变化类型、受影响的节点、以及相关信息的详细信息。
MutationObserver API 的使用场景包括但不限于:
- 监视 DOM 中特定元素的属性变化。
- 监视子节点的添加、移除或替换。
- 监视文本内容的变化。
- 实时监视动态加载的内容变化。
通过 MutationObserver,开发者可以更加灵活地监控 DOM 变化,实现更加复杂和高效的 DOM 操作和交互。
5、实现逻辑
1、通过手写组件的方式,将需要添加水印的内容放入组件内
2、通过props传入不同内容,实现自定义水印内容、字体大小、水印之间的间隔等等
3、通过canvas来画出水印文字,最后将canvas画出的内容转换为图片
4、通过MutationObserverAPI来监听水印元素被删除、被修改属性的变化
6、代码
1、使用水印组件的代码
<template><div class="container"><Watermark text="版权所有123"><div class="item">秘密</div></Watermark></div> </template><script setup> import { ref, reactive } from 'vue' import Watermark from '@/components/Watermark/index.vue' </script><style scoped lang="scss"> .container {width: 100%;display: flex;justify-content: space-around;.item {width: 300px;height: 300px;text-align: center;line-height: 300px;color: #fff;font-size: 50px;background-color: #266fff;} } </style>
2、水印组件代码
<template><div class="watermark" ref="parent"><slot></slot></div> </template><script setup> import directive from '@/directive' import { ref, reactive, computed, onMounted, onUnmounted } from 'vue' const parent = ref(null) const props = defineProps({text: {type: String,required: true,default: 'watermark',},fontSize: {type: Number,default: 32,},// 水印的间隔gap: {type: Number,default: 20,}, }) const watermarkBg = props => {return computed(() => {const canvas = document.createElement('canvas')// 视口分辨率 确保当窗口大小变化时 画出的内容不模糊const devicePixelRatio = window.devicePixelRatio || 1const fontSize = props.fontSize * devicePixelRatioconst font = fontSize + 'px serif'const ctx = canvas.getContext('2d')// 获取文字宽度ctx.font = fontconst { width } = ctx.measureText(props.text)const canvasSize = Math.max(100, width) + props.gap * devicePixelRatiocanvas.width = canvasSizecanvas.height = canvasSizectx.translate(canvas.width / 2, canvas.height / 2)ctx.rotate((Math.PI / 180) * -45)ctx.fillStyle = 'rgba(0,0,0,0.3)'ctx.font = fontctx.textAlign = 'center'ctx.textBaseline = 'middle'ctx.fillText(props.text, 0, 0)return {// 转换为base64格式的图片base64: canvas.toDataURL(),size: canvasSize / devicePixelRatio,}}) } const bg = watermarkBg(props)// 重置水印 // 如果把水印删掉了,就重新调用这个方法 生成一个新的div // 如果把水印的样式改了,重新调用这个方法,生成一个新的div // 因此这个函数可能会反复调用 造成多个水印的生成 let div = null function resetWatermark() {if (!parent.value) return// 清除div防止生成多个水印if (div) {div.remove()}const { base64, size } = bg.valuediv = document.createElement('div')div.style.position = 'absolute'div.style.backgroundImage = `url(${base64})`div.style.backgroundSize = `${size}px ${size}px`div.style.backgroundRepeat = 'repeat'div.style.zIndex = 999// 实现点击穿透div.style.pointerEvents = 'none'div.style.inset = 0parent.value.appendChild(div) }// 生成水印的元素什么时候调用 // 1、onMounted的时候 onMounted(() => {// 首次生成水印resetWatermark()// 观察水印元素ob.observe(parent.value, {// 观察父元素上所有的子节点childList: true,// 观察父元素上所有的子节点的子节点subtree: true,// 观察父元素身上的属性变化attributes: true,}) })// 2、当被删除或修改样式的时候 // 这个观察器在当元素发生变化的时候执行 const ob = new MutationObserver(entries => {for (const entry of entries) {// 处理删除for (const node of entry.removedNodes) {if (node === div) {resetWatermark()}}// 处理修改if (entry.target === div) {resetWatermark()}} })// 注意细节,在页面卸载的时候取消观察 onUnmounted(() => {ob.disconnect() }) </script><style scoped lang="scss"> .watermark {position: relative; } </style>
相关文章:
前端开发攻略---Vue实现防篡改水印的效果。删除元素无效!更改元素属性无效!支持图片、元素、视频等等。
1、演示 2、水印的目的 版权保护:水印可以在图片、文档或视频中嵌入作者、品牌或版权所有者的信息,以防止未经授权的复制、传播或使用。当其他人使用带有水印的内容时,可以追溯到原始作者或版权所有者,从而加强版权保护。 身份识…...
在Go语言中复制sync类型
sync包提供了基本的同步原语,例如互斥锁、条件变量和等待组。对于所有这些类型,有一个硬性规则要遵循:它们永远不应该被复制。让我们来理解下这个原理和可能发生的问题。 我们将创建一个线程安全的数据结构来存储计数器。它将包含一个map[string]int,表示每个计数器的当前值…...
Golang | Leetcode Golang题解之第25题K个一组翻转链表
题目: 题解: func reverseKGroup(head *ListNode, k int) *ListNode {hair : &ListNode{Next: head}pre : hairfor head ! nil {tail : prefor i : 0; i < k; i {tail tail.Nextif tail nil {return hair.Next}}nex : tail.Nexthead, tail my…...
【初学】前后端flask+vue组合GET案例
【CSDN 目录配置很不好用】 一、python配置 pip install flaskpip install flask-cors 二、vue配置 1.下载node.js 2.安装node.js 3.测试 node -v4.在vue项目文件夹中创建vue项目 npm create vue@latest第一次会安装一个东西,然后输入名称,一路回车 ✔ Project name…...
计算机科学与技术CS考研408资料
在github上整理了考研的一些资料: 内容包括: 参考书数据结构、组成原理、操作系统、计算机网络.408笔记PDF408思维导图408真题2009-2021真题无logo版408真题2029-2023王道真题(持续更新)历年真题考频统计灰灰考研择校࿰…...
ACID模型是什么
ACID模型是什么 ACID模型是数据库管理系统中保证事务处理安全性的一组特性。ACID是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)四个英文单词的…...
【Linux】基础IO----理解缓冲区
> 作者:დ旧言~ > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:理解缓冲区 > 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安! > 专栏选自:Linux初阶 > 望…...
java学习之路-继承
文章目录 前言 目录 1.1继承的概念 1.2继承有什么好处,为何要继承 1.3继承的语句 1.4父类成员的访问 1.4.1 子类中访问父类的成员变量 1.4.2 子类中访问父类的成员方法 1.5 super关键字 2.子类构造方法 2.1如何创建构造方法 2.2创建构造方法 3.super和this 【相同点…...
Linux系统——Elasticsearch企业级日志分析系统
目录 前言 一、ELK概述 1.ELK简介 2.ELK特点 3.为什么要使用ELK 4.完整日志系统基本特征 5.ELK工作原理 6.Elasticsearch介绍 6.1Elasticsearch概述 6.2Elasticsearch核心概念 7.Logstash介绍 7.1Logstash简介 7.2Logstash主要组件 8.Kibana介绍 8.1Kibana简介 …...
多协议接入视频汇聚EasyCVR平台vs.RTSP安防视频EasyNVR平台:设备分组的区别
EasyCVR视频融合云平台则是旭帆科技TSINGSEE青犀旗下支持多协议接入的视频汇聚融合共享智能平台。平台可支持的接入协议比EasyNVR丰富,包括主流标准协议,有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海…...
Spring Security Oauth2 之 理解OAuth 2.0授权流程
1. Oauth 定义 1.1 角色 OAuth定义了四个角色: 资源所有者 一个能够授权访问受保护资源的实体。当资源所有者是一个人时,它被称为最终用户。 资源服务器 托管受保护资源的服务器能够使用访问令牌接受和响应受保护的资源请求。 客户 代表资源所有…...
mysql题目4
tj11: select count(*) 员工总人数 from tb_dept a join tb_employee b on a.deptnob.deptno where a.dname 市场部...
GFS部署实验
目录 1、部署环境 编辑 2、更改节点名称 3、准备环境 4、磁盘分区,并挂载 5. 做主机映射--/etc/hosts/ 6. 复制脚本文件 7. 执行脚本完成分区 8. 安装客户端软件 1. 安装解压源包 2. 创建gfs 3. 安装 gfs 4. 开启服务 9、 添加节点到存储信任池中 1…...
最前沿・量子退火建模方法(1) : subQUBO讲解和python实现
前言 量子退火机在小规模问题上的效果得到了有效验证,但是由于物理量子比特的大规模制备以及噪声的影响,还没有办法再大规模的场景下应用。 这时候就需要我们思考,如何通过软件的方法怎么样把大的问题分解成小的问题,以便通过现在…...
如何在Linux部署MeterSphere并实现公网访问进行远程测试工作
文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…...
postgis导入shp数据时“dbf file (.dbf) can not be opened.“
作者进行矢量数据导入数据库中出现上述报错 导致报错原因 导入的shp文件路径太深导入的shp文件名称或路径中有中文将需要导入数据的shp 文件、dbf 文件、prj 等文件放在到同一个文件夹内,且名字要一致;导入失败: 导入成功:...
StarUML笔记之从C++代码生成UML图
StarUML笔记之从C代码生成UML图 —— 2024-04-14 文章目录 StarUML笔记之从C代码生成UML图1.安装C插件2.准备好一个C代码文件放某个路径下3.点击Reverse Code选择项目文件夹4.拖动(Class)到中间画面可以形成UML5.另外一种方式:双击Type Hierarchy,然后…...
sizeof()和strlen
一、什么是sizeof() sizeof()是一个在C和C中广泛使用的操作符,用于计算数据类型或变量所占内存的字节数。它返回一个size_t类型的值,表示其操作数所占的字节数。 在使用时,sizeof()可以接收一个数据类型作为参数,也可以接收一个…...
Python学习笔记13 - 元组
什么是元组 元组的创建方式 为什么要将元组设计为不可变序列? 元组的遍历...
[leetcode]remove-duplicates-from-sorted-list-ii
. - 力扣(LeetCode) 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 示例 1: 输入:head [1,2,3,3,4,4,5] 输出:[1,2,5]示例 2&…...
共享内存和Pytorch中的Dataloader结合
dataloader中通常使用num_workers来指定多线程来进行数据的读取。可以使用共享内存进行加速。 代码地址:https://github.com/POSTECH-CVLab/point-transformer/blob/master/util/s3dis.py 文章目录 1. 共享内存和dataloader结合1.1 在init中把所有的data存储到共享内…...
分享 WebStorm 2024 激活的方案,支持JetBrains全家桶
大家好,欢迎来到金榜探云手! WebStorm公司简介 JetBrains 是一家专注于开发工具的软件公司,总部位于捷克。他们以提供强大的集成开发环境(IDE)而闻名,如 IntelliJ IDEA、PyCharm、和 WebStorm等。这些工具…...
Android OOM问题定位、内存优化
一、OOM out of memory:简称OOM,内存溢出,申请的内存大于剩余的内存而抛出的异常。 对于Android平台,广义的OOM主要是以下几种类型 JavaNativeThread 线程数的上限默认为32768,部分华为设备的限制是500通常1000左右…...
棋盘(c++题解)
题目描述 有一个m m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。 任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的) ,你只能向上、下、 左、右…...
滑动窗口例题
一、209:长度最小的子数组 209:长度最小的子数组 思路:1、暴力解法:两层for循环遍历,当sum > target时计算子数组长度并与result比较,取最小的更新result。提交但是超出了时间限制。 class Solution {public int minSubArray…...
智过网:注册安全工程师注册有效期与周期解析
在职业领域,各种专业资格认证不仅是对从业者专业能力的认可,也是保障行业安全、规范发展的重要手段。其中,注册安全工程师证书在安全生产领域具有举足轻重的地位。那么,注册安全工程师的注册有效期是多久呢?又是几年一…...
腐蚀Rust 服务端搭建架设个人社区服务器Windows教程
腐蚀Rust 服务端搭建架设个人社区服务器Windows教程 大家好我是艾西,一个做服务器租用的网络架构师也是游戏热爱者。最近在steam发现rust腐蚀自建的服务器以及玩家还是非常多的,那么作为服务器供应商对这商机肯定是不会放过的哈哈哈! 艾西这…...
蓝桥杯备赛:考前注意事项
考前注意事项 1、DevCpp添加c11支持 点击 工具 - 编译选项 中添加: -stdc112、万能头文件 #include <bits/stdc.h>万能头文件的缺陷:y1 变量 在<cmath>中用过了y1变量。 #include <bits/stdc.h> using namespace std;// 错误示例 …...
111111111111
111111111111...
uniapp 卡片勾选
前言 公司的app项目使用的uniapp,项目里有一个可勾选的卡片功能,效果图如下: 找了一圈没找到什么太好的组件,于是就自己简单写了一个,记录一下。避免以后还会用到 代码 <template><view class"card-…...
网站建设及数据分析/成都调查事务所
k个男生和k个女生站成一列,前面k个是男生,后面k个是女生,从第一个男生开始报数,报到队列最后一个同学,循环到队首继续报,并且如果一个同学报到的数是m,这个同学就出列,然后后面的同学…...
新疆巴州建设局网站/百度seo公司哪家最好
1.什么是直方图?直方图是数字图像处理中一个简单而重要的常用工具,它从总体上刻画了一幅图像的灰度内容。具体来说,直方图描述的是图像中具有各灰度级的出现的概率(像素的个数),其横坐标为灰度级,纵坐标为图像中具有该灰度级的像素个数。由于灰度级的大小为0-255&am…...
中国建设网官方网站下载e路最新版官方/郑州网站建设推广有限公司
事件流处理 事件流处理(Event Stream Processing,ESP)和复杂事件处理(Complex Event Processing,CEP)的系统,它可以监测事件流并当特定事件发生时触发某些行动——可看作是把数据库反过来&#…...
嘉兴市建设派出所网站/百度推广哪家做的最好
原文链接:http://doc.redisfans.com/pub_sub/index.html Redis的Pub/Sub模型可以应对工作中的一些简单应用,涉及到复杂应用还是推荐使用诸如RabbitMQ或ActiveMQ等。publish channel message 将message发送到指定频道,例:publis…...
网页制作作业源代码/百家号关键词seo优化
安装 从 http://www.sonatype.org/nexus/ 下载最新的 Nexus 压缩包, 现在已经不提供war包的下载 解压到服务器目录, 例如我是放到/opt/nexus 目录下, 如果不打算用root用户启动服务, chown到相应的用户 注: 如果有点强迫症, 不想chown整个目录, 至少要chown以下目录的权限: …...
建立网站不公开/免费个人网站源码
设计模式6大原则-单一职责原则 定义:一个类应该只有一个引起变化的原因。一个类只负责一项职责 如果一个类有多个职责,这些职责耦合在一起,当一个职责发生变化时,可能影响其他的职责。 如: 一个类有 A、B、C 等职责…...