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、使用水印组件的代码
html<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、水印组件代码
html<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 || 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) 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格式的图片 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.value div = 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 = 0 parent.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>