【VUE】前端实现防篡改的水印

news2024/10/6 10:27:50

效果

在这里插入图片描述

在这里插入图片描述

水印的作用

图片加水印的操作一般是由后端来完成,有些站点保护的知识产权的类型可能比较多,不仅仅是图片,可能还有视频、文字等等,对于不同类型的对象添加水印后端操作比较复杂,所有有些站点逐步的让前端去进行水印添加的操作。

前端框架

React框架

如果用React框架来进行开发就比较简单,在Ant Design 库里面有一个水印组件Watermark,通过这个组件你可以给一个区域加上一个水印。区域内容没有限制,如图片、文字、视频等等添加水印都是可以的。
在这里插入图片描述

Vue 框架

Ant Design Vue Element UI 暂时没有水印组件。所以需要自己开发,基本思路如下:

  1. 生成水印 :使用canvas.toDataURL()生成base64水印图片数据,将水印图片数据赋值到水印div上。
  2. 防止篡改
    • 监控篡改 : 使用 MutationObserver.observe 监控水印元素、属性、内容、子元素变化,然后改变依赖数据flag.value++;
    • 重新添加水印:定义响应式依赖数据const flag = ref(0);, 使用 watchEffect 自动追踪依赖flag.value;,重新添加水印元素

代码

App.vue

<template>
  <div class="container">
    <WatermarkComponent text="少莫千华">
      <div class="content" style="background-color: red;">
        <img  src="./assets/LA.png"/>
      </div>
    </WatermarkComponent>
    <WatermarkComponent text="少莫千华">
      <video controls class="video" src= './assets/LA.mp4'></video>
    </WatermarkComponent>
  </div>
</template>

<script>
import WatermarkComponent from './components/WatermarkComponent.vue'

export default {
  name: 'App',
  components:{
    WatermarkComponent
  }
}
</script>

<style scoped>
.container{
  width: 100%;
  display: flex;
  justify-content: space-around;
  position: relative;
}
.content{
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  margin: 3px;
}
img{
  height: 100%;
  width: 100%;
  object-fit:cover;
}
video {
  height: 110%;
  width: 100%;
  object-fit:fill;
}
.watermark-container {
  position: relative;
  flex-basis: 50%;
  box-sizing: border-box;
}
video{
  position: absolute;
  flex-basis: 50%;
  box-sizing: border-box;
}

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

WatermarkComponent.vue

<template>
  <div class="watermark-container" ref="parentRef">
    <slot></slot>
  <!-- 添加一个div,填充整个区域,设置水印背景,重复 -->
  </div>
</template>

<script setup>
import { onMounted, onUnmounted,defineProps, ref, watchEffect } from 'vue';
import useWatermarkBg from './useWatermarkBg';

const props = defineProps( {
  text: {
    type: String,
    required: true,
    default: '少莫千华',
  },
  fontSize:{
    type: Number,
    default: 40,
  },
  gap:{
    type:Number,
    default:20,
  },
});
const bg = useWatermarkBg(props);
const parentRef = ref(null);
// 定义一个依赖
const flag = ref(0);
let div;

//挂载以后添加水印
//监控元素变化、元素属性变化,防止篡改
//动态生成水印元素div
watchEffect(()=>{
  flag.value;
  if(!parentRef.value){
    return ;
  }
  if(div)
  {
    div.remove();
  }
  const {base64,styleSize} = bg.value;
  div = document.createElement('div');
  div.style.backgroundImage = `url(${base64})`;
  div.style.backgroundSize = `${styleSize}px ${styleSize}px`;
  // 重复平铺
  div.style.backgroundRepeat = 'repeat'; 
  // 覆盖到同级的上一个元素
  div.style.zIndex = 9999;
  // 绝对定位
  div.style.position = 'absolute';
  // 设置边距
  div.style.inset = 0;
  //div.style.left = 0;
  //div.style.right = 0;
  //div.style.top = 0;
  //div.style.bottom = 100;
  //将水印添加到 .watermark-container 元素中
  parentRef.value.appendChild(div);
});

onMounted(()=>{
    //监控元素属性、子元素、内容、元素本身变化
    let ob =  new MutationObserver((records)=>{
    console.log(records);
    for(const record of records) {
      // 判断删除的节点
      for(const dom of record.removedNodes) {
        // 判断节点是不是水印
        if(dom === div) {
          //删除水印元素触发 watchEffect
          console.log('删除了水印元素');
          // 修改依赖值,触发 watchEffect 重新运行
          flag.value++;
          return;
        }
      }
      //修改水印元素属性触发 watchEffect
      if(record.target === div){
        console.log('修改了水印属性');
        // 修改依赖值,触发 watchEffect 重新运行
        flag.value++;
        return;
      }
      //生产环境考虑到其他内容,完善,如 ZIndex 等等
    }
  });
  // 监听 parentRef.value的变化
  // 监听内容:childList、attributes、subtree
  ob.observe(parentRef.value,{
    childList: true,
    attributes : true,
    subtree: true,
  });
  onUnmounted(()=>{
    ob && ob.disconnect();//取消监听
    div = null;
  });
});



</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.wartermark-container{
  position: relative;
}
</style>

生成水印图像 useWatermarkBg.js

import { computed } from 'vue';

export default function useWatermarkBg(props) {
  return computed(() => {
    const canvas = document.createElement('canvas');
    const devicePixelRatio = window.devicePixelRatio || 1;
    const fontSize = props.fontSize * devicePixelRatio;
    const font = fontSize + 'px serif';
    const ctx = canvas.getContext('2d');
    // 获取文字宽度
    ctx.font = font;
    const {width} = ctx.measureText(props.text);
    const canvasSize = Math.max(100, width) + props.gap * devicePixelRatio;
    console.log(canvasSize + 'px');
    canvas.width = canvasSize;
    canvas.height = canvasSize;
    ctx.translate(canvas.width/2, canvas.height/2);
    //倾斜文本45°
    ctx.rotate((Math.PI/180) * -45);
    ctx.fillStyle  = 'rgba(0,0,0,0.3)';
    ctx.font = font;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(props.text,0,0);
    return {
      base64:canvas.toDataURL(),
      size:canvasSize,
      styleSize:canvasSize/devicePixelRatio,
    };
  });
}

资源

LA.png

在这里插入图片描述

LA.mp4

演讲开始素材

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

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

相关文章

内网穿透-————SSH远程连接树莓派

前言 文章目录 前言内网穿透实现公网SSH远程连接树莓派1. 在树莓派上安装[cpolar客户端](https://www.cpolar.com/)2. 在树莓派浏览器中输入本地9200端口3. 在公共互联网的电脑的命令行界面输入命令 内网穿透实现公网SSH远程连接树莓派 随着科技的进步和信息技术的发展&#x…

c++ boost circular_buffer

boost库中的 circular_buffer顾名思义是一个循环缓冲器&#xff0c;其 capcity是固定的当容量满了以后&#xff0c;插入一个元素时&#xff0c;会在容器的开头或结尾处删除一个元素。 circular_buffer为了效率考虑&#xff0c;使用了连续内存块保存元素 使用固定内存&#x…

降本增效,除了裁员企业还能做什么?

现在面临企业裁员的新闻时&#xff0c;我们已经非常平静淡定了。短短几个月&#xff0c;裁员潮已经从互联网高科技科技行业&#xff0c;蔓延至金融、零售、汽车等行业&#xff0c;从新闻变成常态。裁员「常态化」背后&#xff0c;是企业面临的经营压力和对降本增效的关注。 随…

绿色项目管理:为环境和效益双赢

绿色项目管理&#xff1a;为环境和效益双赢 在21世纪的今天&#xff0c;我们正面临着各种全球性的环境问题&#xff0c;从气候变化到资源枯竭。作为项目经理&#xff0c;我们有责任和机会确保我们的项目对环境的影响最小&#xff0c;并在可能的情况下为环境做出积极的贡献。 …

geeemap学习总结(1)——Anaconda-VSCode-geemap环境安装与配置

配置conda geemap 环境 通过Anaconda配置geemap环境较为方便&#xff0c;首先需在系统中完成 Anaconda安装。创建名为geemap的环境conda create -n geemap切换到新建的环境conda activate geemap安装geemap依赖包conda install -c conda-forge geemap 安装mambaconda install …

算法通关村——原来如此简单

题目 给定一个二叉树的根节点root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧 所能看到的节点值 示例 思路 将当前层的最后一个节点放入集合中&#xff08;从右侧看到的节点值就是每一层的最后一个节点值&#xff09; 代码实现…

C语言预备知识

安装Visual studio 官方网址 https://visualstudio.microsoft.com/zh-hans/ 选择第一个社区版本&#xff08;免费&#xff09; 下载完成后打开安装包 安装完成后会自动打开程序选择c项目然后安装即可&#xff08;c兼容c&#xff09; 安装完成后启动程序注意这里需要注册也可…

AIGC:【LLM(四)】——LangChain+ChatGLM:本地知识库问答方案

文章目录 一.文件加载与分割二.文本向量化与存储1.文本向量化(embedding)2.存储到向量数据库 三.问句向量化四.相似文档检索五.prompt构建六.答案生成 LangChainChatGLM项目(https://github.com/chatchat-space/langchain-ChatGLM)实现原理如下图所示 (与基于文档的问答 大同小…

Kafka3.4 SASL/kerberos/ACL 证以及 SSL 加密连接

Kafka3.4 SASL/kerberos ACL 证以及 SSL 加密连接 序 前面我们使用 kafka3.3.1 on zookeeper 的模式进行多网段监听的 kafka 集群&#xff0c;顺便搭建起 kafkaui 后发现一些问题&#xff0c;我们 kafka 集群没有连接认证&#xff0c;万一谁知道了我们的 kafka 连接地址&…

如何免费申请SSL证书

如何免费申请SSL证书 文章目录 如何免费申请SSL证书前言1. 向域名平台申请SSL证书1.1 购买“免费证书” 2. 进一步进行创建证书设置2.1 对证书的关联域名进行补充 3. 云解析DNS3.1 进行验证信息 前言 我们可以成功地将自己购买的域名&#xff0c;绑定到连接本地群晖NAS的数据隧…

Element plus el-table 鼠标滚动失灵的问题及解决办法

Bug&#xff1a;ElementUI el-table 鼠标滚轮下滑动失灵的情况 我测出来的这个问题条件很苛刻&#xff0c;需要达到以下几个条件才会触发&#xff1a; 1.element plus&#xff08;其他版本没试&#xff09; 2.el-table-column组件有fixed属性时 3.template标签中有el-butto…

【EI/SCOPUS会议征稿】第三届数字经济与计算机应用国际学术会议(DECA2023)

第三届数字经济与计算机应用国际学术会议&#xff08;DECA2023&#xff09; The 3rd International Conference on Digital Economy and Computer Application 第三届数字经济与计算机应用国际学术会议 (DECA 2023) 将于2023年9月22-24日在中国上海召开。会议主题主要围绕数…

【2.1】Java微服务:详解Hystrix

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f49e;当前专栏&#xff1a; 深度学习 ✨特色专栏&#xff1a; 知识分享 &…

每天五分钟机器学习:梯度下降算法和正规方程的比较

本文重点 梯度下降算法和正规方程是两种常用的机器学习算法,用于求解线性回归问题。它们各自有一些优点和缺点,下面将分别对它们进行详细的讨论。 区别 1. 梯度下降算法是一种迭代的优化算法,通过不断迭代调整参数来逼近最优解。它的基本思想是根据目标函数的梯度方向,沿…

Vue-组件二次封装

本次对el-input进行简单封装进行演示 封装很简单&#xff0c;就给激活样式的边框(主要是功能) 本次封装主要使用到vue自带的几个对象 $attrs&#xff1a;获取绑定在组件上的所有属性$listeners: 获取绑定在组件上的所有函数方法$slots&#xff1a; 获取应用在组件内的所有插槽 …

MATLAB的设置路径

在主页下的 或者在命令行输入path&#xff0c;命令行会出现所有路径 必须要将某些函数.m文件以及一些类文件包含在路径当中&#xff0c;否则在脚本代码中输入代码时&#xff0c;不会有代码提示

【Azure】office365邮箱测试的邮箱账号因频繁连接邮箱服务器而被限制连接 引起邮箱显示异常

azure微软office365邮箱会对频繁连接自身邮箱服务器的IP地址进行&#xff0c;连接邮箱服务器IP限制&#xff0c;也就是黑名单&#xff0c;释放时间不确定&#xff0c;但至少一天及以上。 解决办法&#xff0c;换一个IP&#xff0c;或者新注册一个office365邮箱再重试。 以下是…

Mysql5.7 、MySQL 8.0 加密、解密函数

PASSWORD 8.0版本取消了&#xff0c;只能在5.7中使用 返回字符串str的加密版本&#xff0c;41位长的字符串&#xff0c;加密结果不可逆 格式 select PASSWORD(xxx) from DUAL;MD5 5.7和8.0 都支持 返回字符串str的MD5加密后的值&#xff0c;若参数为null&#xff0c;则…

不同风格Tabs

风格 通过type设置风格&#xff0c;支持三种风格card、borderCard、line 核心代码 组件双向绑定 modelVal: {type: Number | String,required: true}, model: {prop: modelVal,event: change} this.$emit(change, this.active) 代码 <template><div:class"[…

【链表OJ 1】移除链表元素val

大家好&#xff0c;欢迎来到我的博客&#xff0c;此题是关于链表oj的第一题&#xff0c;此后还会陆续更新博客&#xff0c;如有错误&#xff0c;欢迎大家指正。 来源:https://leetcode.cn/problems/remove-linked-list-elements/description/ 题目: 方法一:定义prev和cur指针…