前端开发攻略---Vue实现防篡改水印的效果。删除元素无效!更改元素属性无效!支持图片、元素、视频等等。

1、演示

2、水印的目的

  1. 版权保护:水印可以在图片、文档或视频中嵌入作者、品牌或版权所有者的信息,以防止未经授权的复制、传播或使用。当其他人使用带有水印的内容时,可以追溯到原始作者或版权所有者,从而加强版权保护。

  2. 身份识别:水印可以用作作者或品牌的标识符,使观众能够轻松识别内容的来源。这对于在社交媒体上分享内容或在网络上发布作品的个人、摄影师、设计师或公司来说尤为重要。

  3. 品牌宣传:水印可以帮助提升品牌知名度和曝光度。通过在图片或视频中添加品牌标识,品牌可以在内容被分享或传播时获得额外的宣传效果。

  4. 内容跟踪:通过在内容中添加水印,可以跟踪内容的传播和使用情况。这对于了解内容在网络上的传播路径、受众和影响力等信息是有帮助的。

3、实话实说

如果通过我们技术层面要解决这种安全或者是版权之类的问题,只能说起一定的作用,增加那些不怀好意的人操作难度。

要说能够完全防止住,那是不可能的,一定是技术手段和非技术手段相结合,双管齐下。这样才能确保万无一失。

总之:防君子不防小人

 4、API介绍(MutationObserver

MutationObserver API 是 Web API 的一部分,用于监视 DOM 树的变化。它允许开发者注册一个回调函数,该函数在指定的 DOM 节点或子树发生变化时被调用。MutationObserver 是一个强大的工具,可以用于监视并响应 DOM 中的变化,而无需使用传统的事件监听器。

主要的组成部分包括:

  1. MutationObserver 对象:用于观察 DOM 树的变化。通过创建 MutationObserver 的实例并传入一个回调函数,可以开始监视指定节点或节点集合的变化。

  2. 观察目标:要监视的 DOM 节点或节点集合。MutationObserver 可以观察单个节点、节点列表,甚至整个文档的变化。

  3. 回调函数:MutationObserver 注册的回调函数在观察的节点发生变化时被调用。回调函数接收一个 MutationRecord 对象数组作为参数,该数组包含描述每个变化的信息。

  4. 变化记录(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 || 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>
  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你可以使用`vue-draggable-resizable`插件来实现一个类似网格布局的拖拽排序效果。首先,确保你已经在项目中安装了该插件。 接下来,你可以按照以下步骤实现你的需求: 1. 创建一个父容器,设置其为flex布局,并且设置`flex-wrap: wrap`以支持换行。 ```html <div class="grid-container"> <!-- 子元素 --> </div> ``` 2. 在父容器中循环渲染子元素,每个子元素都是一个正方形盒子。 ```html <div class="grid-container"> <div v-for="(item, index) in items" :key="index" class="grid-item"> <!-- 子元素内容 --> </div> </div> ``` 3. 使用`vue-draggable-resizable`插件将子元素变为可拖拽和可调整大小的元素。你可以在`mounted`钩子中初始化插件。 ```javascript import VueDraggableResizable from 'vue-draggable-resizable' export default { components: { VueDraggableResizable }, data() { return { items: [ // 子元素数据 ] } }, mounted() { // 初始化 vue-draggable-resizable 插件 VueDraggableResizable.mount(this.$refs.container) } } ``` 4. 在子元素的模板中使用`vue-draggable-resizable`组件,并设置合适的属性。 ```html <div class="grid-container" ref="container"> <vue-draggable-resizable v-for="(item, index) in items" :key="index" :w="100" :h="100" :x="item.x" :y="item.y" :isDraggable="true" :isResizable="true" @dragStop="onDragStop(index, $event)" @resizeStop="onResizeStop(index, $event)" > <!-- 子元素内容 --> </vue-draggable-resizable> </div> ``` 5. 在组件中添加对应的事件处理方法,用于更新子元素的位置和大小。 ```javascript export default { // ... methods: { onDragStop(index, event) { // 更新子元素的位置 this.items[index].x = event.x this.items[index].y = event.y }, onResizeStop(index, event) { // 更新子元素的大小 this.items[index].w = event.width this.items[index].h = event.height } } } ``` 通过以上步骤,你可以实现一个基于`vue-draggable-resizable`插件的flex布局类似网格的拖拽排序效果,其中子元素为正方形盒子。记得根据你的需求,调整样式和属性值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bbamx.

谢谢您

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值