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

效果

【VUE】前端实现防篡改的水印_第1张图片

水印的作用

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

前端框架

React框架

如果用React框架来进行开发就比较简单,在Ant Design 库里面有一个水印组件Watermark,通过这个组件你可以给一个区域加上一个水印。区域内容没有限制,如图片、文字、视频等等添加水印都是可以的。
【VUE】前端实现防篡改的水印_第2张图片

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>
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>


<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

演讲开始素材

你可能感兴趣的:(语言-HTML,VUE,前端,vue.js,Watermark,watchEffect,Mutation,Observer,canvas)