当前位置: 首页 > article >正文

【vue】vue + vant实现上传图片添加水印

目录

方法1:使用HTML2canvas

说明:

优点

缺点

依赖安装

方法2:使用canvas结合vant中组件

增加水印方法

在vue组件中使用

要点


方法1:使用HTML2canvas

使用html2canvas来处理水印的生成,需要就给水印元素转换为canvas图像并叠加到上传的图片。

<template>
  <div>
    <van-uploader :after-read="afterRead" />
    <!-- 水印元素 -->
    <div id="watermark" style="display: none;">
      <div style="font-size: 20px; color: rgba(0, 0, 0, 0.3); transform: rotate(-45deg); position: absolute; top: 50%; left: 50%; transform-origin: center;">
        水印文本
      </div>
      <!-- 如果需要图片水印,可以在这里添加图片元素 -->
      <img src="path/to/watermark.png" style="position: absolute; bottom: 10px; right: 10px; width: 100px; height: 100px; opacity: 0.3;" />
    </div>
  </div>
</template>

<script>
import { ref } from 'vue';
import html2canvas from 'html2canvas';

export default {
  setup() {
    const afterRead = async (file) => {
      // 创建 canvas 元素
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');

      // 设置 canvas 尺寸为图片尺寸
      const img = await new Promise((resolve) => {
        const img = new Image();
        img.src = file.content;
        img.onload = () => resolve(img);
      });
      canvas.width = img.width;
      canvas.height = img.height;

      // 绘制原始图片
      ctx.drawImage(img, 0, 0);

      // 获取水印元素并转换为 canvas
      const watermarkElement = document.getElementById('watermark');
      const watermarkCanvas = await html2canvas(watermarkElement, {
        logging: false,
        scale: window.devicePixelRatio, // 高清屏支持
        useCORS: true, // 启用跨域
      });

      // 将水印 canvas 绘制到主 canvas 上
      ctx.drawImage(watermarkCanvas, 0, 0, canvas.width, canvas.height);

      // 将 canvas 转换为 Blob 并替换文件内容
      canvas.toBlob((blob) => {
        file.file = blob;
        file.content = URL.createObjectURL(blob);
      }, 'image/png');
    };

    return {
      afterRead,
    };
  },
};
</script>

    说明:

    html2canvas 配置

    logging: false:禁用日志输出。

    scale: window.devicePixelRatio:支持高清屏。

    useCORS: true:启用跨域,确保图片资源可以被正确加载。

    绘制水印

    使用 html2canvas 将水印元素转换为 canvas 图像。

    将生成的水印 canvas 图像绘制到主 canvas 上。

    生成最终图片

    将主 canvas 转换为 Blob,并替换上传的文件内容。

    优点
    1. 灵活性高:可以将任何 HTML 元素转换为 canvas 图像,这意味着你可以使用 HTML 和 CSS 创建非常复杂的水印效果,比如带有图标、多行文本、不同字体和颜色等。

    2. 可视化设计:可以直接在页面上设计水印的样式,所见即所得,不需要通过代码去想象最终效果。

    3. 兼容性较好html2canvas 会尽可能地将页面元素渲染成与浏览器中显示一致的图像,减少了因手动绘制带来的兼容性问题。

    缺点
    1. 依赖外部库:需要引入 html2canvas 库,增加了项目的依赖和体积。

    2. 性能问题:将 HTML 元素转换为 canvas 图像的过程可能会有一定的性能开销,尤其是在水印元素复杂或者页面元素较多的情况下。

    3. 配置复杂:可能需要调整 html2canvas 的各种配置选项,以确保生成的图像符合预期,比如设置背景色、处理透明度等。

    依赖安装
    cnpm install compressorjs html2canvas

    方法2:使用canvas结合vant中组件

    使用纯canvas操作直接绘制水印文本

    vant的van-uploader组件提供了after-read事件,在其中调用水印处理函数

    增加水印方法
    /**
     * 添加水印
     * @param {blob} file
     * @param {string} el
     * @returns {Promise}
     */
    export async function addWaterMarker(file, el = '#markImg') {
      return new Promise(async (resolve, reject) => {
        try {
          // 先压缩和旋转图片
          file = await compressor(file);
          // 将文件 blob 转换成图片
          let img = await blobToImg(file);
          // 创建 canvas 画布
          let canvas = document.createElement('canvas');
          canvas.width = img.naturalWidth;
          canvas.height = img.naturalHeight;
          let ctx = canvas.getContext('2d');
    
          // 填充上传的图片
          ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    
          // 生成水印图片
          const markEle = document.querySelector(el);
          const markWidth = markEle.clientWidth;
          const scale = canvas.width * 0.25 / markWidth;
          // 先缩放水印再转成图片
          markEle.style.transform = `scale(${scale})`;
          const markImg = await htmlToCanvas(markEle);
    
          // 填充水印
          ctx.drawImage(
            markImg,
            canvas.width - markImg.width - 15 * scale,
            canvas.height - markImg.height - 15 * scale,
            markImg.width,
            markImg.height
          );
    
          // 将 canvas 转换成 blob
          canvas.toBlob((blob) => resolve(blob));
        } catch (error) {
          reject(error);
        }
      });
    }
    
    function blobToImg(blob) {
      return new Promise((resolve, reject) => {
        let reader = new FileReader();
        reader.addEventListener('load', () => {
          let img = new Image();
          img.src = reader.result;
          img.addEventListener('load', () => resolve(img));
        });
        reader.readAsDataURL(blob);
      });
    }
    
    export function htmlToCanvas(el, backgroundColor = 'rgba(0,0,0,.1)') {
      return new Promise(async (resolve, reject) => {
        try {
          const markImg = await html2canvas(el, {
            scale: 2,
            allowTaint: false,
            useCORS: true,
            backgroundColor,
          });
          resolve(markImg);
        } catch (error) {
          reject(error);
        }
      });
    }
    
    /**
     * 压缩和旋转图片
     * @param {blob} file
     * @param {number} quality
     * @param {number} maxWidth
     * @returns {Promise}
     */
    export function compressor(file, quality = 0.6, maxWidth = 750) {
      return new Promise((resolve) => {
        new Compressor(file, {
          maxWidth,
          quality,
          success: resolve,
          error(err) {
            console.log(err.message);
          },
        });
      });
    }
    在vue组件中使用
    <template>
      <div>
        <van-uploader
          v-model="fileList"
          multiple
          :after-read="afterRead"
          :before-read="beforeRead"
        />
        <!-- 水印内容 -->
        <div id="markImg" style="display: none;">
          <div style="font-size: 14px; color: rgba(255, 255, 255, 0.3);">
            水印文字
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import { addWaterMarker, compressor } from '@/utils/watermark';
    import Compressor from 'compressorjs';
    
    export default {
      data() {
        return {
          fileList: [], // vant 中图片上传的双向绑定
        };
      },
      methods: {
        // 上传前处理
        async beforeRead(file) {
          return new Promise(async (resolve, reject) => {
            if (Array.isArray(file)) {
              if (file.length > 5) {
                this.$toast('一次最多上传5张,请分批次上传!');
                reject();
                return;
              }
              let blobs = [];
              for (const f of file) {
                // 大于 512k 的图片则先压缩
                if (f.size > 512 * 1024 && f.type.includes('image/')) {
                  f = await this.compressor(f);
                }
                // 添加水印
                let blob = await addWaterMarker(f);
                blob.name = f.name;
                blobs.push(blob);
              }
              resolve(blobs);
            } else {
              // 大于 512k 的图片则先压缩
              if (file.size > 512 * 1024 && file.type.includes('image/')) {
                file = await this.compressor(file);
              }
              const blob = await addWaterMarker(file);
              blob.name = file.name;
              resolve(blob);
            }
          });
        },
        // 上传后处理
        async afterRead(file) {
          // 处理上传逻辑
          console.log('上传成功', file);
        },
        // 压缩图片
        async compressor(file) {
          return new Promise((resolve) => {
            new Compressor(file, {
              quality: 0.6,
              success: resolve,
              error(err) {
                console.log(err.message);
              },
            });
          });
        },
      },
    };
    </script>
    要点

    addWaterMarker:添加水印的核心方法,负责将水印内容绘制到图片上。

    beforeRead:在图片上传前处理,包括压缩和添加水印。

    afterRead:图片上传后的处理逻辑。

    使用了 html2canvas 将水印内容转换为图片。

    使用了 compressorjs 对图片进行压缩。

    码字不易,各位大佬点点赞呗


    http://www.kler.cn/a/613967.html

    相关文章:

  1. 使用matlab进行分位数回归
  2. 基于Vue的低代码可视化表单设计器 FcDesigner 3.2.11更新说明
  3. 机器学习和深度学习的关系
  4. 自动化逆向框架使用(Objection+Radare2)
  5. Manus:通用智能体的架构革命与产业破局
  6. 记一次系统单点登录、模拟web系统登录方式的开发过程,使用AES加密
  7. Arduino、ESP32驱动GUVA-S12SD UV紫外线传感器(光照传感器篇)
  8. C 标准库 – 头文件
  9. git_merge
  10. 12_JavaScript_实现日期
  11. 21.Excel自动化:如何使用 xlwings 进行编程
  12. 大模型——使用Ollama本地部署Gemma-3-27B大模型,基于LangChain分析PDF文档
  13. 解决“Generic family ‘sans-serif‘ not found”问题
  14. ansible介绍以及安装
  15. 【Golang】补充:占位符、转义字符、错误处理
  16. Rust 面向对象
  17. 基于Zookeeper的微服务配置管理与灰度发布实战指南
  18. Elasticsearch:理解政府中的人工智能 - 应用、使用案例和实施
  19. SQLark导出功能详解|轻松管理数据库数据与结构
  20. 鸿蒙富文本实践