uni-app为图片添加自定义水印(升级版)

前置内容

uni-app为图片添加自定义水印(解决生成图片不全问题)

UI 升级

现在水印样式变成这样了:

代码

html 复制代码
<template>
  <canvas v-if="waterMarkParams.display" canvas-id="waterMarkCanvas" :style="canvasStyle"/>
</template>

<script>
  export default {
    data() {
      return {
        waterMarkParams: {
          display: false, // 控制 canvas 创建与销毁
          canvasWidth: 300, // 默认宽度
          canvasHeight: 225, // 默认高度
        }
      }
	},
    computed: {
      canvasStyle() {
        return {
          position: 'fixed', // 移除到屏幕外
          left: '9999px',
          width: this.waterMarkParams.canvasWidth + 'px',
          height: this.waterMarkParams.canvasHeight + 'px'
        }
      }
	},
    methods: {
      chooseImage() {
        uni.chooseImage({
          sourceType: ['all'],
          success: async ({ tempFilePaths, tempFiles }) => {
            // 这里就得到了带水印的图片路径列表
             const imgFileArr = await this.callAddWaterMark(tempFilePaths)
          }
        })
      },
      // 因为有可能在相册中选择多个图片,所以这里要依次生成水印
      async callAddWaterMark(imgPathArr) {
        let results = []
        if(imgPathArr.length > 0) {
          let addIndex = 0
          while(addIndex < imgPathArr.length) {
            const tempFilePath = await this.addWaterMark(imgPathArr[addIndex])
            results.push(tempFilePath)
            addIndex = addIndex + 1
          }
        }
        return results
      },
      addWaterMark(src) {
        return new Promise((resolve, reject) => {
          // 获取图片信息,配置 canvas 尺寸
          uni.getImageInfo({
            src,
            success: res => {
              // 修复部分手机(如红米9)手机屏幕比较窄拍摄出来的图片水印压缩着覆盖的问题
              this.waterMarkParams.canvasWidth = Math.max(res.width, 886);
              this.waterMarkParams.canvasHeight = res.height;
              this.waterMarkParams.display = true
              console.log('当前图片信息waterMarkParams:', this.waterMarkParams);
              // 等待 canvas 元素创建
              this.$nextTick(() => {
                let context = uni.createCanvasContext("waterMarkCanvas", this);
                /* 绘制 */
                const { canvasWidth, canvasHeight } = this.waterMarkParams
                // 绘制前清空画布
                context.clearRect(0, 0, canvasWidth, canvasHeight);
                // 将图片src放到cancas内,宽高必须为图片大小
				context.drawImage(src, 0, 0, canvasWidth, canvasHeight, canvasWidth, canvasHeight);
                
                // 防伪码的位置
                const fangweiY = 320;
                // 保证水印能完整显示出来
                const [x, y] = [canvasWidth / 2, canvasHeight - (fangweiY + 100)];
                context.translate(x, y);
                
                // 标记一下坐标系,更容易理解
                /* context.beginPath();
                context.lineWidth = 2;
                context.strokeStyle = 'white';
                context.moveTo(- x, 0);
                context.lineTo(x, 0);
                context.moveTo(0, -y);
                context.lineTo(0, canvasHeight - y);
                context.stroke();
                context.closePath(); */
                
                fillText(context, 0, 70, '上海市·金山区', 'bold 56px "Microsoft YaHei"', 'white', 'center');
                fillText(context, -20, 220, '16:08', 'bold 150px "Microsoft YaHei"', 'white', 'right');
                fillRect(context, -4, 100, 8, 120, '#346DFF');
                fillText(context, 20, 140, '2024.04.18', 'bold 40px "Microsoft YaHei"', 'white', 'left');
                fillText(context, 20, 210, '星期四  晴 21℃', 'bold 40px "Microsoft YaHei"', 'white', 'left');
                fillText(context, 0, fangweiY, `防伪:JY20240418160748XIAOMI`, 'bold 40px "Microsoft YaHei"', 'white', 'center');
                /* 绘制marker图标 */
                // 蓝色外圆
                fillCircle(context, -260, 30, 30, '#346DFF');
                // 白色内圆
                fillCircle(context, -260, 30, 12, 'white');
                // 蓝色三角
                fillTriangle(context, -260, 78, -235, 48, -285, 48, '#346DFF');
                
                // 坐标原点移动到画布右上角
				context.translate(canvasWidth / 2, -y);
                const [rectWidth, rectHeight, rectX, rectY, lineWidth, lineHeight] = [200, 90, -550, 60, 300, 6];
                // 右上角矩形
                fillRect(context, rectX, 60, rectWidth, rectHeight, '#346DFF');
                // 右上角下划线
                fillRect(context, rectX + rectWidth, rectY + rectHeight - lineHeight, lineWidth, lineHeight, '#346DFF');
                // 右上角姓名
                fillText(context, rectX + rectWidth / 2, 120, '鹏北海', 'bold 40px "Microsoft YaHei"', 'white', 'center');
                // 右上角手机号码
                fillText(context, rectX + rectWidth + lineWidth / 2, 120, '15888888888', 'bold 34px "Microsoft YaHei"', 'white', 'center');
                // 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常
                setTimeout(() => {
                  // 本次绘画完重开开始绘画,并且在绘画完毕之后再保存图片,不然页面可能会出现白屏等情况
                  context.draw(false, () => {
                    console.log('!!!!!开始绘画', canvasWidth, canvasHeight);
                    uni.canvasToTempFilePath({
                      canvasId: "waterMarkCanvas",
                      fileType: "jpg",
                      width: canvasWidth,
                      height: canvasHeight,
                      destWidth: canvasWidth,
                      destHeight: canvasHeight,
                      success: ({ tempFilePath }) => {
                        console.log('绘制成功', tempFilePath);
                        this.waterMarkParams.display = false
                        resolve(tempFilePath)
                      },
                      fail: err => {
                        reject(err)
                        console.log(err);
                      }
                    }, this)
                  })
                }, 1000);
              })
            }
          })
        })
        
        // 绘制文字
        function fillText(context, x, y, content, font, fontStyle, textAlign) {
          // 保存当前绘图状态
          context.save();
          // 设置字体样式
          context.font = font
          // 设置文字颜色为白色
          context.fillStyle = fontStyle
          // 设置文字水平居中对齐
          context.textAlign = textAlign
          context.fillText(content, x, y)
          // 恢复到之前保存的绘图状态,清除样式设置
          context.restore();
        }

        // 绘制圆
        function fillCircle(context, x, y, r, fillStyle) {
          // 保存当前绘图状态
          context.save();
          context.beginPath();
          context.arc(x, y, r, 0, 2 * Math.PI);
          context.fillStyle = fillStyle;
          context.fill();
          context.closePath();
          // 恢复到之前保存的绘图状态,清除样式设置
          context.restore();
        }

        // 绘制三角形
        function fillTriangle(context, x1, y1, x2, y2, x3, y3, fillStyle) {
          // 保存当前绘图状态
          context.save();
          context.beginPath();
          context.moveTo(x1, y1);
          context.lineTo(x2, y2);
          context.lineTo(x3, y3);
          context.fillStyle = fillStyle;
          context.fill();
          context.closePath();
          // 恢复到之前保存的绘图状态,清除样式设置
          context.restore();
        }

        // 绘制矩形
        function fillRect(context, x, y, width, height, fillStyle) {
          // 保存当前绘图状态
          context.save();
          context.fillStyle = fillStyle;
          context.fillRect(x, y, width, height);
          // 恢复到之前保存的绘图状态,清除样式设置
          context.restore();
        }
      },
    }
  }
</script>
相关推荐
笃励13 分钟前
Java面试题二
java·开发语言·python
jyan_敬言21 分钟前
【Linux】Linux命令与操作详解(一)文件管理(文件命令)、用户与用户组管理(创建、删除用户/组)
linux·运维·服务器·c语言·开发语言·汇编·c++
FL162386312925 分钟前
[C#]C# winform部署yolov11-pose姿态估计onnx模型
开发语言·yolo·c#
笑非不退36 分钟前
C++ 异步编程 并发编程技术
开发语言·c++
儒雅的烤地瓜1 小时前
JS | JS中判断数组的6种方法,你知道几个?
javascript·instanceof·判断数组·数组方法·isarray·isprototypeof
道爷我悟了1 小时前
Vue入门-指令学习-v-on
javascript·vue.js·学习
27669582921 小时前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东
爱写代码的刚子1 小时前
C++知识总结
java·开发语言·c++
martian6651 小时前
QT开发:基于Qt实现的交通信号灯模拟器:实现一个带有倒计时功能的图形界面应用
开发语言·qt
冷琴19961 小时前
基于java+springboot的酒店预定网站、酒店客房管理系统
java·开发语言·spring boot