# vue3 实现前端生成水印效果

vue3 实现前端生成水印效果

首先一点哈,就是单纯web前端生成水印只能作为警示使用,如果享彻底防住几乎是不可能的,有无数种方式去掉web前端生成的水印,所以这种方式只当是一个君子协议吧。

编写水印组件

首先直接把这部分封装成一个组件吧,我这边直接上代码了。

创建一个 waterMark.vue 文件,用来编写水印组件:

javascript 复制代码
<template>
  <div class="watermark-container" ref="parentRef">
    <slot></slot>
  </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import useWaterMarkBg from './waterMarkBg';
const props = defineProps({
  text: {
    type: String,
    default: "版权所有"
  },
  fontSize: {
    type: Number,
    default: 25,
  },
  gap: {
    type: Number,
    default: 20,
  },
  color: {
    type: String,
    default: 'rgba(201, 35, 35, 0.5)'
  }
})



let div;
const bg = useWaterMarkBg(props);
const parentRef = ref();

const ob = new MutationObserver((entries) => {
  for (const entry of entries) {
    for (const node of entry.removedNodes) {
      if (node === div) {
        resetWatermark();
        return;
      }
    }
    if (entry.target === div) {
      resetWatermark();
    }
  }
})

onMounted(() => {
  resetWatermark();
  ob.observe(parentRef.value, {
    childList: true,
    subtree: true,
  })
})

onUnmounted(() => {
  ob.disconnect();
})

// 重置水印
const resetWatermark = () => {
  if (!parentRef.value) { return }
  if (div) {
    div.remove();
  }
  const { base64, size } = bg.value.value;
  div = document.createElement('div');
  div.style.position = 'absolute';
  div.style.backgroundImage = `url(${base64})`;
  div.style.backgroundSize = `${size.width}px ${size.height}px`;
  div.style.backgroundRepeat = "repeat";
  div.style.pointerEvents = 'none';
  div.style.zIndex = '9999';
  div.style.inset = 0;
  parentRef.value.appendChild(div);
}

</script>
<style scoped lang="scss">
.watermark-container {
  position: relative;
}
</style>

可以接受四个参数,如果不够可以自己加,分别是 text 水印文本内容、fontSize 水印文本大小、gap 水印文本间隔、color 水印文本颜色。

然后在水印组件加载完成的时候调用 resetWatermark 重置水印方法实现添加水印。

水印是动态生成的图片,最后创建了一个动态的div加上页面的,因为还想尽可能的防止一下水印删除,所以说在中途检测了一下dom修改情况,如果修改了,比如删除了div,或者是修改了div的样式,那么就重置水印,重新添加一遍。

其中在组件中还是用了 useWaterMarkBg 方法,下面代码是 waterMarkBg.js 文件的内容,可以根据自己的业务需求适当的修改:

javascript 复制代码
import { ref, computed } from 'vue';

/**
 * 创建水印背景图片的 composable 函数
 * @param {Object} options - 水印配置选项
 * @param {string} options.text - 水印文字内容
 * @param {number} options.fontSize - 字体大小
 * @param {number} options.gap - 水印间隔
 * @param {string} options.color - 文字颜色,默认为半透明灰色
 * @param {number} options.rotate - 旋转角度,默认为 -15 度
 * @param {string} options.fontFamily - 字体,默认为 Arial
 * @returns {Object} 返回包含 base64 和 size 的响应式对象
 */
function useWaterMarkBg(options = {}) {
  // 默认参数
  const defaultOptions = {
    text: '版权所有',
    fontSize: 25,
    gap: 20,
    color: 'rgba(201, 35, 35, 0.5)',
    rotate: -15,
    fontFamily: 'Arial, sans-serif'
  };

  // 合并用户参数和默认参数
  const waterMarkOptions = ref({ ...defaultOptions, ...options });

  // 计算水印尺寸和 base64 图片
  const waterMarkInfo = computed(() => {
    const { text, fontSize, gap, color, rotate, fontFamily } = waterMarkOptions.value;
    
    // 创建 canvas 元素
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    if (!ctx) {
      throw new Error('无法获取 canvas 上下文');
    }
    
    // 设置字体
    ctx.font = `${fontSize}px ${fontFamily}`;
    
    // 获取文字宽度
    const textWidth = ctx.measureText(text).width;
    
    // 计算 canvas 尺寸(包含文字和间隙)
    const width = textWidth + gap * 2;
    const height = fontSize * 2 + gap * 2;
    
    canvas.width = width;
    canvas.height = height;
    
    // 重置上下文(因为 canvas 尺寸改变了)
    ctx.font = `${fontSize}px ${fontFamily}`;
    ctx.fillStyle = color;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    
    // 保存当前状态
    ctx.save();
    
    // 移动到 canvas 中心并旋转
    ctx.translate(width / 2, height / 2);
    ctx.rotate((Math.PI / 180) * rotate);
    
    // 绘制文字
    ctx.fillText(text, 0, 0);
    
    // 恢复之前的状态
    ctx.restore();
    
    // 转换为 base64
    const base64 = canvas.toDataURL('image/png');
    
    return {
      base64,
      size: {
        width,
        height
      }
    };
  });

  // 如果传入的 options 发生变化,可以更新水印
  function updateOptions(newOptions) {
    waterMarkOptions.value = { ...waterMarkOptions.value, ...newOptions };
  }

  return {
    value: waterMarkInfo,
    updateOptions
  };
}

export default useWaterMarkBg;

水印组件的使用

使用的时候就很简单了,引入一下,然后包裹一下需要添加水印的dom就可以了:

javascript 复制代码
    <!-- 默认红色水印 -->
    <water-mark text="严禁传播">
      <div class="img-con">
        <img src="../../assets/imgs/watermark/1.jpg" alt="图片1">
      </div>
    </water-mark>

    <!-- 蓝色水印 -->
    <water-mark text="禁止复制" :fontSize="25" color="rgba(30, 144, 255, 0.3)">
      <div class="img-con">
        <img src="../../assets/imgs/watermark/2.jpg" alt="图片2">
      </div>
    </water-mark>

    <!-- 绿色水印 -->
    <water-mark text="测试水印" :fontSize="25" color="rgba(50, 205, 50, 0.4)">
      <div class="img-con">
        <img src="../../assets/imgs/watermark/1.jpg" alt="图片3">
      </div>
    </water-mark>

效果

好了,大体就这个样子,还是,水印这个很容易删除,懂得人,删的很快,最好从源头解决,只要后端返回前端的是原文件,那么就可以从浏览器获取到没有水印的内容。

相关推荐
福兮说4 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
郑州光合科技余经理5 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6666 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
Dovis(誓平步青云)6 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan6 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_7 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒7 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技7 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu8 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub
默_笙9 小时前
🛴 从散件到整机:DeepAgents 与 Agent 身上预留的那些"插槽"(前置介绍)
前端·javascript