服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服务器之家 - 编程语言 - JavaScript - vue.js - Vue3问题:如何在页面上添加水印?

Vue3问题:如何在页面上添加水印?

2024-03-19 14:50程序员大澈 vue.js

水印的添加不仅仅满足于添加,有时候还要能防止用户恶意篡改,时刻保证水印的功效。这次问题我分为了两种情况:一种是仅添加水印仅可,另一种是添加水印且要防篡改。

Vue3问题:如何在页面上添加水印?

1. 需求分析

为了防止网站信息被盗用,以及维护版权标识,常常需要在页面、图片或视频上添加独特水印,以作区分。

同时,水印的添加不仅仅满足于添加,有时候还要能防止用户恶意篡改,时刻保证水印的功效。

所以,这次问题我分为了两种情况:一种是仅添加水印仅可,另一种是添加水印且要防篡改。

下面我将把实现一一列出。

Vue3问题:如何在页面上添加水印?

2. 实现步骤

(1)仅添加水印情况

对于仅需要添加水印的情况,直接使用第三方UI库中的水印组件即可,简单快速。

当然,我们也可以选择自己造轮子,用Canvas来画,但是对于工作而言,我觉得这样应该尽量避免。

这里我使用ElementPlus 2.4.0中,新出的Watermark水印组件作为例子。

实现代码:

<template>
<el-watermark
: 
: 
image="https://element-plus.org/images/element-plus-logo.svg"
>
<div   />
</el-watermark>
</template>

效果如下:

Vue3问题:如何在页面上添加水印?

当然要注意的是,ElementPlus的依赖版本一定要是2.4.0之后的。

(2)添加水印且要防篡改情况

像ElementPlus提供的水印组件,是不支持防篡改功能的。

也就是说,如果有用户通过浏览器的控制台进行元素属性的修改,是可以把页面中的水印隐藏的。所以为了安全起见,是很需要做防篡改处理的。

为了保证自定义水印的灵活性,这里我使用了原生js的写法,并且代码参考了渡一官方大佬的文章。

简言之,就是利用Canvas绘制水印图像,以及利用MutationObserver对象来监听Dom节点或其子节点的变化以实现防篡改处理。

代码实现如下:

先写一个hook函数useWatermarkBg,在其中用Canvas绘制水印图像。

import { computed } from 'vue';
export default function useWatermarkBg (props) {
return computed(() => {
// 创建一个 canvas
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;
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,
};
});
}

再封装一个水印公共组件WaterMark,在其中调用useWatermarkBg函数生成水印图像,以及添加水印、做防篡改处理。

在mounted中,创建MutationObserver实例,监听水印DOM节点的变化,在节点删除或属性修改时设置依赖,发出重新添加水印的通知。

在watchEffect中,进行收集依赖,只要依赖变化了,它就会重新添加水印图像,达到防篡改效果。

值得一提的是,因为添加水印的原理是给页面添加一个绝对定位的重复水印背景的div,但是,如果这样我们就不能点击div下层的元素了。

所以,这里还用了一个叫pointerEvents的css属性,设置值为none,从而使元素不会接收鼠标事件,鼠标事件会透过元素传递到下层的元素上。

<template>
<div   ref="parentRef">
<slot></slot>
</div>
</template>

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

const props = defineProps({
text: {
type: String,
required: true,
default: 'watermark',
},
fontSize: {
type: Number,
default: 40,
},
gap: {
type: Number,
default: 20,
},
});

const bg = useWatermarkBg(props);
const parentRef = ref(null);
const flag = ref(0); // 声明一个依赖
let div;

watchEffect(() => {
flag.value; // 将依赖放在 watchEffect 里
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.pointerEvents = 'none';
parentRef.value.appendChild(div);
});

// 防篡改处理
let ob;
onMounted(() => {
ob = new MutationObserver((records) => {
for (const record of records) {
for (const dom of record.removedNodes) {
if (dom === div) {
flag.value++; // 删除节点的时候更新依赖
return;
}
}
if (record.target === div) {
flag.value++; // 修改属性的时候更新依赖
return;
}
}
});
ob.observe(parentRef.value, {
childList: true,
attributes: true,
subtree: true,
});
});

onUnmounted(() => {
ob && ob.disconnect();
div = null;
});
</script>

最后,在需要添加水印的页面直接使用即可。

<template>
<water-mark>
<video src="https://www.51cto.com/article/@/assets/a.mp4" controls   height="500"></video>
</water-mark>
</template>

<script setup>
import WaterMark from "@/components/WaterMark.vue"
</script>

3. 问题详解

(1)关于MutationObserver总结

MutationObserver 是 JavaScript 中的一个内置对象,它提供了一种监视 DOM(文档对象模型)树变化的能力。

MutationObserver 允许开发者注册一个回调函数,当观察的 DOM 节点或子节点发生变化时,会触发这个回调函数。这些变化可以包括节点的添加、移除、属性的变化、文本内容的改变等。

使用 MutationObserver 可以监视特定的 DOM 元素或整个文档,并在相关变化发生时执行相应的操作。这对于实时监测页面变化、自动化测试、实现响应式 UI 等场景非常有用。

下面是一个简单的示例,演示如何使用 MutationObserver 监测某个元素的子节点变化:

// 目标元素
var targetElement = document.getElementById('target-element');

// 创建一个 MutationObserver 实例
var observer = new MutationObserver(function(mutationsList, observer) {
// 当变化发生时执行的回调函数
for (var mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('子节点发生变化');
console.log(mutation.addedNodes); // 添加的节点列表
console.log(mutation.removedNodes); // 移除的节点列表
}
}
});

// 配置观察选项
var config = { childList: true };

// 开始观察目标元素
observer.observe(targetElement, config);

在上述示例中,我们首先通过 getElementById 获取目标元素,然后创建一个 MutationObserver 实例,传入一个回调函数作为参数。回调函数会在目标元素的子节点发生变化时被调用。我们可以在回调函数中根据 mutationsList 的内容进行相应的处理。在这个示例中,我们只关注子节点的变化,并打印相关信息到控制台。

最后,我们通过调用 observe 方法来开始观察目标元素的变化。在 config 对象中,我们将 childList 属性设置为 true,表示我们要监测子节点的变化。

需要注意的是,MutationObserver 是一个异步的机制,它会在变化发生后才触发回调函数。这意味着在开始观察之前的变化可能不会被捕获到。如果需要在开始观察之前立即获取当前状态的变化,可以在创建 MutationObserver 实例后,使用 observer.takeRecords() 方法来获取当前的变化记录。

(2)关于pointer-events的总结

pointer-events 是一种 CSS 属性,它控制元素对鼠标事件的响应方式。它可以设置在任何 HTML 元素上,并具有以下几个可能的取值:

  • auto:元素对鼠标事件作出默认的响应。即元素会根据自身的样式和内容对鼠标事件做出适当的响应。
  • none:元素对鼠标事件不做出响应。即元素不会接收鼠标事件,鼠标事件会透过元素传递到下层的元素上。这相当于将元素变为不可点击,即使它位于页面上方或遮挡其他元素。
  • visiblePainted:元素对鼠标事件做出响应,但鼠标事件不会穿透元素,而是传递到下层的元素上。这意味着元素会接收鼠标事件,但不会阻止下层元素对鼠标事件的响应。
  • visibleFill:元素对鼠标事件做出响应,并会阻止鼠标事件传递到下层的元素上。这意味着元素会完全接收鼠标事件,并阻止下层元素对鼠标事件的响应。

pointer-events 属性对于创建交互式的页面元素非常有用,可以控制元素是否能够接收和响应鼠标事件。通过将其设置为 none,可以使元素在外观上可见,但不会干扰下层元素的交互。

需要注意的是,pointer-events 属性的兼容性有限,可能不支持所有浏览器或旧版本的浏览器。在使用时,建议先检查兼容性并提供备用方案或降级策略。

原文地址:https://www.toutiao.com/article/7310523187857916450/

延伸 · 阅读

精彩推荐
  • vue.jsVue仿百度搜索功能

    Vue仿百度搜索功能

    这篇文章主要为大家详细介绍了Vue仿百度搜索功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Rebright-崇明11472021-12-21
  • vue.js详解为什么Vue中的v-if和v-for不建议一起用

    详解为什么Vue中的v-if和v-for不建议一起用

    这篇文章主要介绍了面试官:为什么Vue中的v-if和v-for不建议一起用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    动感超人,3852021-12-30
  • vue.jsvue-cli4.0多环境配置变量与模式详解

    vue-cli4.0多环境配置变量与模式详解

    这篇文章主要介绍了vue-cli4.0多环境配置变量与模式详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考...

    紫藤萝yu9632021-12-23
  • vue.jsvuex的使用和简易实现

    vuex的使用和简易实现

    这篇文章主要介绍了vuex的使用和简易实现,帮助大家更好的理解和使用vuex,感兴趣的朋友可以了解下...

    Charon6392021-12-28
  • vue.jsvue 实现图片懒加载功能

    vue 实现图片懒加载功能

    这篇文章主要介绍了vue 实现图片懒加载功能的方法,帮助大家更好的理解和使用vue,感兴趣的朋友可以了解下...

    熊大的小跟班11512021-12-23
  • vue.js基于vue的video播放器的实现示例

    基于vue的video播放器的实现示例

    这篇文章主要介绍了基于vue的video播放器的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们...

    地瓜哥9972022-01-21
  • vue.jsvue 项目@change多个参数传值多个事件的操作

    vue 项目@change多个参数传值多个事件的操作

    这篇文章主要介绍了vue 项目@change多个参数传值多个事件的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    一行注释9752022-01-11
  • vue.js如何封装Vue Element的table表格组件

    如何封装Vue Element的table表格组件

    这篇文章主要介绍了如何封装Vue Element的table表格组件,帮助大家更好的理解和使用vue框架,感兴趣的朋友可以了解下...

    豫见陈公子8212022-01-17