【uniapp】图片添加canvas水印

news2025/3/12 2:51:36

目录

  • 需求&背景
  • 实现
    • 地理位置
    • 添加水印
  • ios补充

需求&背景

需求:拍照后给图片添加水印, 水印包含经纬度、用户信息、公司logo等信息。
效果图:
在这里插入图片描述

方案:使用canvas添加水印。
具体实现:上传图片组件是项目里现有的,主要还是使用uni.chooseImage,这里不做赘述。
在上传图片组件中增加一个参数判断是否添加水印,在获取到图片、上传到后端之前对图片进行加工。

实现

在模板中添加canvas。
template:

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

props:

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

其他的一些数据

//这个放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开发调试会显示不出来。

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

// 添加水印
    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,就是添加了水印的图片

let waterUrl = await this.getWaterMark(lists[i].url);
lists[i].filePath = waterUrl;

ios补充

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2311662.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

贪吃蛇身匀速运动模型

通用运动模型 我们已知斜线为移动的距离 d d d&#xff0c; x x x轴总偏移量为 d x dx dx&#xff0c; y y y轴总偏移量为 d y dy dy&#xff0c;在一帧当中&#xff0c;我们也知道能走的距离为 m d md md。那么作为一般的运动模型&#xff0c;该如何确定我们进行移动的方向呢&…

npm 执行安装报错

Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-deps to accept an incorrect (and potentially broken) dependency resolution. 原因​ 主要的原因是 npm7 以上的版本&#xff0c;新增了一个对等依赖的特性&#xff0c;在以…

笔记五:C语言编译链接

Faye&#xff1a;孤独让我们与我们所爱的人相处的每个瞬间都无比珍贵&#xff0c;让我们的回忆价值千金。它还驱使你去寻找那些你在我身边找不到的东西。 ---------《寻找天堂》 目录 一、编译和链接的介绍 1.1 程序的翻译环境和执行环境 1.1.1 翻译环境 1.1.2 运行环境 …

【c语言概述、数据类型、运算符与表达式精选题】

c语言概述、数据类型、运算符与表达式精选题 一、易错题1.1&#x1f384; c程序的执行1.2&#x1f384; c程序的基本组成单元1.3&#x1f384; c程序的组成1.4&#x1f384; 5种基本类型数据类型长度1.5&#x1f384; C语言关键字1.6&#x1f384; 整型常量1.7&#x1f384; 合…

200个前卫街头氛围涂鸦艺术水墨颜料手绘笔迹飞溅PNG免扣迭加纹理素材 VANTABLACK TEXTURES

探索 Vantablack 200 纹理包&#xff1a;您获得前卫、高分辨率纹理的首选资源。非常适合旨在为其作品添加原始都市氛围的设计师。这些透明迭加层使用简单&#xff0c;但非常有效&#xff0c;只需单击几下&#xff0c;即可将您的设计从普通变为非凡。准备好用既酷又百搭的质地来…

深度学习模型Transformer核心组件—自注意力机制

第一章&#xff1a;人工智能之不同数据类型及其特点梳理 第二章&#xff1a;自然语言处理(NLP)&#xff1a;文本向量化从文字到数字的原理 第三章&#xff1a;循环神经网络RNN&#xff1a;理解 RNN的工作机制与应用场景(附代码) 第四章&#xff1a;循环神经网络RNN、LSTM以及GR…

nature genetics | SCENT:单细胞多模态数据揭示组织特异性增强子基因图谱,并可识别致病等位基因

–https://doi.org/10.1038/s41588-024-01682-1 Tissue-specific enhancer–gene maps from multimodal single-cell data identify causal disease alleles 研究团队和单位 Alkes L. Price–Broad Institute of MIT and Harvard Soumya Raychaudhuri–Harvard Medical S…

基于数据挖掘的疾病数据可视化分析与预测系统

【大数据】基于数据挖掘的疾病数据可视化分析与预测系统&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 &#x1f4cc; 技术核爆点&#xff1a;✔️ Python全栈开发Flask高能框架 ✔️ 爬虫技术…

《AI大模型专家之路》No.2:用三个模型洞察大模型NLP的基础能力

用三个模型洞察大模型NLP的基础能力 一、项目概述 在这个基于AI构建AI的思维探索项目中&#xff0c;我们实现了一个基于BERT的中文AI助手系统。该系统集成了文本分类、命名实体识别和知识库管理等功能&#xff0c;深入了解本项目可以让读者充分了解AI大模型训练和推理的基本原…

Spring Boot集成Minio笔记

一、首先配置MinIO 1、MinIO新建Bucket&#xff0c;访问控制台如图 创建访问密钥(就是账号和密码) 二、集成mino添加Minio客户端依赖 1.maven构建方式在pom.xml引入jar <dependency><groupId>io.minio</groupId><artifactId>minio</artifactI…

HCIA-路由重分布

一、路由重分布是指在同一个网络中&#xff0c;将一种路由协议所学习到的路由信息导入到另一种路由协议中的技术&#xff0c;实现通信。 二、实验 1、配置 AR1AR2AR3sy sy AR1 int g 0/0/1 ip add 192.168.1.254 24 int g 0/0/0 ip add 10.1.1.1 24 rip version 2 net 192.16…

【项目】nnUnetv2复现

作者提出一种nnUNet(no-new-Net)框架,基于原始的UNet(很小的修改),不去采用哪些新的结构,如相残差连接、dense连接、注意力机制等花里胡哨的东西。相反的,把重心放在:预处理(resampling和normalization)、训练(loss,optimizer设置、数据增广)、推理(patch-based…

【大学生体质】智能 AI 旅游推荐平台(Vue+SpringBoot3)-完整部署教程

智能 AI 旅游推荐平台开源文档 项目前端地址 ☀️项目介绍 智能 AI 旅游推荐平台&#xff08;Intelligent AI Travel Recommendation Platform&#xff09;是一个利用 AI 模型和数据分析为用户提供个性化旅游路线推荐、景点评分、旅游攻略分享等功能的综合性系统。该系统融合…

TCP7680端口是什么服务

WAF上看到有好多tcp7680端口的访问信息 于是上网搜索了一下&#xff0c;确认TCP7680端口是Windows系统更新“传递优化”功能的服务端口&#xff0c;个人理解应该是Windows利用这个TCP7680端口&#xff0c;直接从内网已经具备更新包的主机上共享下载该升级包&#xff0c;无需从微…

恭喜!《哪吒2》明天将荣登世界影坛第六!目前仅差1.81亿元

全球总票房为为20.27亿美元&#xff01;3月8日将荣登世界影坛第六宝座&#xff01; 中国票房 内地票房 中国电影票房、灯塔、猫眼三大数据源加权平均得出《哪吒2》中国内地总票房为144.26亿元人民币。 港澳票房 目前港澳地区没有新的数据显示&#xff0c;按3月6日1905电影网…

e2studio开发RA4M2(15)----配置RTC时钟及显示时间

e2studio开发RA4M2.15--配置RTC时钟及显示时间 概述视频教学样品申请硬件准备参考程序源码下载新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置SWD调试口设置UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_SCI_UART_Open()函数原型回调函数user…

Flink深入浅出之04:时间、水印、TableSQL

深入理解Flink的waterMark的机制、Flink Table和SQL开发 3️⃣ 目标 掌握WaterMark的的原理掌握WaterMark的运用掌握Flink Table和SQL开发 4️⃣ 要点 &#x1f4d6; 1. Flink中的Time概念 对于流式数据处理&#xff0c;最大的特点是数据上具有时间的属性特征 Flink根据时…

MongoDB Compass 使用说明

MongoDB Compass 使用说明 安装工具栏按钮详细介绍Connect(连接)1. New Window&#xff08;新窗口&#xff09;2. Disconnect&#xff08;断开连接&#xff09;3. Import Saved Connections&#xff08;导入保存的连接&#xff09;4. Export Saved Connections&#xff08;导出…

Halcon 算子 一维码检测识别、项目案例

首先我们要明白码的识别思路 把窗口全部关闭读取新的图片图像预处理创建条码模型设置模型参数搜索模型获取条码结果显示条码结果 图像预处理和条码增强 对比度太低&#xff1a; scale_image&#xff08;或使用外部程序scale_image_range&#xff09;,增强图像的对比度图像模糊…

信号完整性基础:高速信号的扩频时钟SSC测试

扩频时钟 SSC 是 Spread Spectrum Clock 的英文缩写&#xff0c;目前很多数字电路芯片都支持 SSC 功能&#xff0c;如&#xff1a;PCIE、USB3.0、SATA 等等。那么扩频时钟是用来做什么的呢&#xff1f; SSC背景&#xff1a; 扩频时钟是出于解决电磁干扰&#xff08;EMI&#…