【uniapp】图片添加canvas水印

目录

需求&背景

需求:拍照后给图片添加水印, 水印包含经纬度、用户信息、公司logo等信息。

效果图:

方案:使用canvas添加水印。

具体实现:上传图片组件是项目里现有的,主要还是使用uni.chooseImage,这里不做赘述。

在上传图片组件中增加一个参数判断是否添加水印,在获取到图片、上传到后端之前对图片进行加工。

实现

在模板中添加canvas。

template:

html 复制代码
<template>
  <view class="md-upload">
    <!-- 其他组件上传图片逻辑 -->
    <canvas v-if="waterMarkParams.display" canvas-id="waterMarkCanvas" :style="canvasStyle" />
  </view>
</template>

props:

javascript 复制代码
props: {
	//其他props参数。。。
    waterMask: { // 是否添加水印
      type: Boolean,
      default: () => false
    }
  },

其他的一些数据

javascript 复制代码
//这个放data
waterMarkParams: {
	display: false, // 控制 canvas 创建与销毁
	canvasWidth: 300, // 默认宽度
	canvasHeight: 225 // 默认高度
},
latitude: "",
longitude: "",
address: "",
orgFullName: "",
userName: "",
currentTime: ""

//这个放computed
canvasStyle() {
return {
		position: 'fixed', // 移除到屏幕外
		left: '9999px',
		width: this.waterMarkParams.canvasWidth + 'px',
		height: this.waterMarkParams.canvasHeight + 'px'
	};
}

地理位置

这里有一个小问题,尝试了很多方法都没办法在success回调中去绘制canvas,退而其次选择在mounted里去获得地理位置,不知道有没有更好的写法欢迎指正。

注:如果要获取地理位置,type只能选择gcj02,而且这个type只适用于app,用h5开发调试会显示不出来。

javascript 复制代码
mounted() {
    uni.getLocation({
      type: 'gcj02',
      geocode: true,
      success: (res) => {
      	//经纬度
        this.longitude = res.longitude; 
        this.latitude = res.latitude;
        //拼接地址
        this.address = res.address.province + res.address.city + res.address.district + res.address.street + res.address.streetNum + res.address.poiName;
      }
    })
  },

添加水印

入参的src是uni.chooseImage的success回调里返回的path

javascript 复制代码
// 添加水印
    async getWaterMark(src) {
    //绘图方法start
      	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);
		}
		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();
		}
		function fillRect(context, x, y, width, height, fillStyle) {
			context.save();
			context.fillStyle = fillStyle;
			context.fillRect(x, y, width, height);
		}
	//绘图方法end
	let that = this;
	that.waterMarkParams.display = true;
      this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss'); //获取当前时间
      if(!this.orgFullName){ //获取运营商信息
        await this.getOrgFullName();
      }
      this.userName = uni.getStorageSync("userInfo").userTitle; //获取用户信息
		return new Promise((resolve, reject) => {
			// 获取图片信息,配置 canvas 尺寸
			uni.getImageInfo({
				src,
				success: (res) => {
            		try {
	           			console.log("成功获取图片信息",res);
						// 修复部分手机(如红米9)手机屏幕比较窄拍摄出来的图片水印压缩着覆盖的问题
						that.waterMarkParams.canvasWidth = Math.max(res.width, 886);
						that.waterMarkParams.canvasHeight = res.height;
						// 等待 canvas 元素创建
						that.$nextTick(async () => {
							let context = uni.createCanvasContext('waterMarkCanvas', that);
							const { canvasWidth, canvasHeight } = that.waterMarkParams;
							// 绘制前清空画布
							context.clearRect(0, 0, canvasWidth, canvasHeight);
							// 将图片src放到cancas内,宽高必须为图片大小
							context.drawImage(src, 0, 0, canvasWidth, canvasHeight, canvasWidth, canvasHeight);
							const fangweiY = 320;
							// 保证水印能完整显示出来 x是画布宽度两倍 y是画布高度减去防伪码位置和字体大小
							const [x, y] = [canvasWidth / 2, canvasHeight - (fangweiY + 100)];

			              // 坐标原点移动到画布左下角
							context.translate(0, canvasHeight);
			              const icon_site = require("./site.png");
			              const icon_camera = require("./camera.png");
			              const icon_icon = require("./icon.png");
			             context.drawImage(icon_site, 40,-360,30,46);
			              fillText(context, 80, -326, this.siteName, '500 40px "Microsoft YaHei"', 'white', 'left');
			              fillRect(context, 40, -290, 10, 260, "#FF0000");
			              fillText(context, 70, -250, "时间:" + this.currentTime, '30px "Microsoft YaHei"', 'white', 'left');
			              fillText(context, 70, -190, "运维商:" + this.orgFullName, '30px "Microsoft YaHei"', 'white', 'left');
			              fillText(context, 70, -130, "经纬度:" + that.longitude + ", " + that.latitude, '30px "Microsoft YaHei"', 'white', 'left');
			              if(that.address.length > 15){ //地址过长时截断显示
			                fillText(context, 70, -70, "地址:" + that.address.slice(0,15), '30px "Microsoft YaHei"', 'white', 'left');
			                fillText(context, 70, -40, "" + that.address.slice(15), '30px "Microsoft YaHei"', 'white', 'left');
			              }else {
			                fillText(context, 70, -70, "地址:" + that.address, '30px "Microsoft YaHei"', 'white', 'left');
			              }
			              //移动到右下角
			              let textLength = that.userName.length * 30;
			              context.translate(canvasWidth, 0);
			              if(that.address.length > 15){
			                context.drawImage(icon_icon, -180, -130,72,35);
			                fillText(context, -40, -100, "光伏", '30px "Microsoft YaHei"', 'white', 'right');
			                context.drawImage(icon_camera, -1 * textLength - 130, -70,38,34);
			                fillText(context, -40, -40, that.userName + "  拍摄", '30px "Microsoft YaHei"', 'white', 'right');
			              }else{
			                context.drawImage(icon_icon, -180, -160,72,35);
			                fillText(context, -40, -130, "光伏", '30px "Microsoft YaHei"', 'white', 'right');
			                context.drawImage(icon_camera, -1 * textLength - 130, -100,38,34);
			                fillText(context, -40, -70, that.userName + "  拍摄", '30px "Microsoft YaHei"', 'white', 'right');
			              }
              
							// 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常
							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('绘制成功');
												that.waterMarkParams.display = false;
												resolve(tempFilePath);
											},
											fail: (err) => {
												reject(err);
												console.log(err);
											}
										},
										that
									);
								});
							}, 1000);
              console.log("完成绘制");
						});
            } catch (error) {
              console.log(error);
            }
					},
          fail: (err) => {
						reject(err);
						console.log(err);
					}
				});
			});
		},

最后用这个方法返回的url替换原本上传时的url,就是添加了水印的图片

javascript 复制代码
let waterUrl = await this.getWaterMark(lists[i].url);
lists[i].filePath = waterUrl;

ios补充

如果ios出现了图片空白,把方法里的定时器时间加长(我从1000加到了2500),uni.chooseImage也选择压缩不要选择原图

相关推荐
人民广场吃泡面10 分钟前
UniApp 运行的微信小程序如何进行深度优化
微信小程序·小程序·uni-app
程序员大澈1 小时前
4个 Vue 路由实现的过程
javascript·vue.js·uni-app
Pro_er1 小时前
Vue3状态管理终极指南:Pinia保姆级教程
vue·前端开发
歡進3 小时前
开源 | Warpvas 实现扭曲的画布
javascript·webgl·canvas
T-shmily4 小时前
Hbuilder X开发微信小程序:利用uni-app和uview UI框架创建项目详细步骤
微信小程序·uni-app
JAVA叶知秋6 小时前
完美解决uni-app打开页面无法自动播放视频的问题
前端·uni-app·音视频
计算机学姐6 小时前
基于Asp.net的零食购物商城网站
vue.js·vscode·后端·mysql·sqlserver·vue·asp.net
程序员小白条8 小时前
【大学生体质】智能 AI 旅游推荐平台(Vue+SpringBoot3)-完整部署教程
java·程序员·vue·springboot·毕设·管理系统·课设
狼性书生9 小时前
uniapp实现的个人中心页面(仿小红书)
uni-app·vue
明耀1 天前
uniapp或者vue 使用serialport
前端·vue.js·uni-app