uni-app移动端使用uni-file-picker上传图片时通过canvas添加拍摄时间等水印信息

实现效果:

添加的照片添加水印信息

实现方式:

将添加水印的方法抽离成组件,为Vue文件,方便复用,在父组件中直接引用即可实现水印效果。

子组件:waterMarker.vue   

此为添加水印的组件文件,实现了在图片中添加水印,本示例在图片中添加了拍摄人、拍摄时间和拍摄经纬度的水印信息,其中拍摄人是从缓存中获取,拍摄时间为实时时间,拍摄经纬度根据mapbox获取当前位置信息的方法获取。此处展示的水印信息可根据个人需求灵活调整,如字体大小、字体颜色、背景颜色等,均支持自定义。

<template>
	<canvas v-if="waterMarkParams.display" canvas-id="waterMarkCanvas" :style="canvasStyle" />
</template>
<script>
	export default {
		data() {
			return {
				userLocation: {
					lng: 124.066873,
					lat: 40.45218,
				}, // 地图图标的默认位置--凤城市市政府
				waterMarkParams: {
					display: false, // 控制 canvas 创建与销毁
					canvasWidth: 300, // 默认宽度
					canvasHeight: 225, // 默认高度
					contentHeight: 170, // 将要被绘制到图像中的矩形的高度(px)
				},
				username: "YourUsername", // 假设的用户名
			}
		},
		computed: {
			// 画布
			canvasStyle() {
				return {
					position: "fixed", // 移除到屏幕外
					left: "9999px",
					width: this.waterMarkParams.canvasWidth + "px",
					height: this.waterMarkParams.canvasHeight + "px",
				};
			},
		},
		mounted() {
			this.getUserLocation(); // 获取当前所在位置
		},
		methods: {
			// 因为有可能在相册中选择多个图片,所以这里要依次生成水印
			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,
									contentHeight
								} =
								this.waterMarkParams;
								// 绘制前清空画布
								context.clearRect(0, 0, canvasWidth, canvasHeight);
								// 将图片src放到cancas内,宽高必须为图片大小
								context.drawImage(
									src,
									0,
									0,
									canvasWidth,
									canvasHeight,
									canvasWidth,
									canvasHeight
								);
								// 设置边框的透明度
								context.setGlobalAlpha(0.3);
								context.beginPath();
								// 绘制底部的白色背景
								context.rect(
									0,
									canvasHeight - contentHeight,
									canvasWidth,
									contentHeight
								);
								// context.setFillStyle("white"); // 白色背景
								context.fill();
								// 设置文字的透明度
								context.setGlobalAlpha(1);
								// 3.绘制底部的文字
								context.setFontSize(32);
								context.setTextAlign("left");
								context.setFillStyle("white"); // 显示字为白色
								context.fillText(`拍摄人:${this.username}`, 50, canvasHeight -
									120);
								context.fillText(
									`拍摄时间:${this.$u.timeFormat(
                  new Date(),
                  "yyyy-mm-dd hh:MM:ss"
                )}`,
									50,
									canvasHeight - 70
								);
								context.fillText(
									`拍摄定位:${this.userLocation.lng},${this.userLocation.lat}`,
									50,
									canvasHeight - 20
								);
								// 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常
								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);
							});
						},
					});
				});
			},
			// 获取用户手机当前位置
			getUserLocation() {
				uni.getLocation({
					type: "wgs84",
					success: (res) => {
						this.userLocation = {
							lng: res.longitude,
							lat: res.latitude,
						};
					},
				});
				this.username = uni.getStorageSync("username"); // 获取localStrorage中的用户名信息
			},
		},
	}
</script>

父组件:使用子组件

父组件中,使用的uni-file-picker进行图片的上传,支持拍摄和本地图库上传,在上传时,通过@select方法,为图片添加水印,添加水印后调用了后端提供的上传接口,这样回显的图片也将是有水印的图片,后端接口这块可根据个人需求灵活变化。

<template>

<view class="upload-text">上传照片</view>
					<view style="width: 400rpx; min-height: 100rpx">
						<uni-file-picker :value="arr" @delete="fileDelete($event)" @select="chooseImage($event)"
							:auto-upload="false" return-type="array" file-extname="png,jpg" limit="3"
							fileMediatype="image" mode="grid" title="最多选择3张图片上传"></uni-file-picker>
					</view>
					<!-- 上报的图片添加水印 -->
					<WaterMarker ref="waterMarker"/>
</template>

<script>
	import WaterMarker from '@/components/mapbox/waterMarker.vue'
	import {
		uploadFiles
	} from "@/api/uploadWaterMarker.js";
export default {
		data() {
			return {
arr: [],
				arrWaterMark: [],
               }
},
components: {
			WaterMarker
		},
methods: {
// 图片--上传图片
			async chooseImage(e) {
				const imgFileArr = await this.$refs.waterMarker.callAddWaterMark(e.tempFilePaths);
				imgFileArr.forEach((el) => {
					this.arr.push({
						url: el,
						extname: el.substring(el.lastIndexOf(".") + 1),
						name: el,
					});
				});
// 此处为调用后端接口,可根据个人情况灵活变化
				let res = await uploadFiles(imgFileArr);
				res.forEach((el) => {
					this.arrWaterMark.push({
						url: service_file + el.data,
						extname: el.data.substring(el.data.lastIndexOf(".") + 1),
						name: el.data,
					});
				});
			},
// 图片--删除照片
			fileDelete(err) {
				this.arr = this.arr.filter((item) => item !== err.tempFile);
			},
// 图片预览
			preview(item, index) {
				uni.previewImage({
					current: index,
					urls: item.map((o) => o.url),
				});
			},
},
}
</script>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python-(opencv)视频转glf

文章目录 前言python-(opencv)视频转glf1. 下载 opencv-python2. cv2&#xff08;OpenCV&#xff09;和imageio的区别3. demo源码 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说…

YashanDB为新质生产力赋能 灌注合肥区域转型源动力

当前&#xff0c;数据要素已成为我国数字经济的“核心引擎”与“关键生产要素”&#xff0c;为全面激发数据要素的价值&#xff0c;各地区正积极探索数据要素交易平台的可行模式&#xff0c;加快在数据要素领域的布局。近日&#xff0c;深圳计算科学研究院崖山数据库系列产品受…

win10系统打开Windows更新是空白的如何解决?

最近装wsl的时候&#xff0c;遇到了这个问题。查阅了很多相关资料&#xff0c;发现导致wsl --install安装不了的主要原因都集中在于windows更新组件损坏导致的&#xff0c;经过排查&#xff0c;我的这个组件确实不能够正常使用&#xff0c;可能是因为之前使用了windows激活工具…

【Java Web】PostMan业务接口测试工具

目录 一、PostMan概述 二、如何安装Postman 三、Postman的基本使用 一、PostMan概述 在生产环境中&#xff0c;一个项目在开发之前、前后端开发工程师通常需要商讨在前后端数据交互时需要采用什么样的规范格式&#xff0c;如&#xff1a;前端向后端发送请求的uri、请求和响…

vue2.0项目安装依赖 sass 报错

1、报错代码&#xff1a; 2、原因&#xff1a;项目有点老&#xff0c;vue2的版本&#xff0c;所以node-sass在npm安装的时候大概率的会安装出错&#xff0c;或下载时间过长&#xff0c;因此考虑用dart-sass来替换。 npm install node-sassnpm:dart-sass3、然后就可以成功运行了…

【PTA】7-1 网红点打卡攻略(C/C++)代码实现 反思

解题细节分析&#xff1a; 0.比较图的两种存储方法&#xff0c;通过邻接矩阵存储更便于查找给定两点之间的关系 1.注意理解清楚题义&#xff1a;“访问所有网红点”中所有不是指攻略中所有&#xff0c;而是存在的全部的网红点 代码见下&#xff1a;// 需要注明的是&#xff…

音频剪辑技巧:音频降噪在线怎么降噪?分享7种录音去除杂音方法

相信很多小伙伴们都有这种苦恼&#xff1a;在编辑音频时&#xff0c;你可能发现即使你使用了价格昂贵的隔音麦克风&#xff0c;在录音中仍然存在呼吸声和咳嗽声。因此&#xff0c;如果要传达清晰干净的声音以表达你的信息&#xff0c;你该如何从录音去除杂音呢&#xff1f;别心…

理清时间复杂度和空间复杂度

目录 复习时间 幂函数 指数函数 对数函数 ​编辑 时间复杂度 推导阶的原则 常见的时间复杂度举例 常数阶 O(1) 对数阶 O(logn) 平方阶 O(n^2) 图像表示 空间复杂度 常见的空间复杂度举例 常数阶 O(1) 线性阶 O(n) 平方阶 O(n^2) 一个算法的优劣主要从算法的执行时间和所需要占…

elementUI的衍生组件,avue的crud表格错位问题

问题描述&#xff1a; 每次从别的页面跳转回来就发现表格显示错位了 一通查 结果发现是有两层表格 解决办法&#xff1a; 根据开发者工具中看到的样式选择器&#xff0c;很粗暴的在全局样式文件中加一个&#xff1a; 效果&#xff1a;

CDP问卷的常见问题

CDP问卷的常见问题可以归纳如下&#xff1a; 哪些企业会收到CDP邀请&#xff1f; 企业会收到来自投资和/或采购机构的邀请&#xff0c;以填写CDP问卷并披露相应的环境管理信息。 未收到邀请的企业可否填报&#xff1f; 未收到邀请的企业可以选择自行填报。他们需发送申请自愿…

[论文阅读笔记33] Matching Anything by Segmenting Anything (CVPR2024 highlight)

这篇文章借助SAM模型强大的泛化性&#xff0c;在任意域上进行任意的多目标跟踪&#xff0c;而无需任何额外的标注。 其核心思想就是在训练的过程中&#xff0c;利用strong augmentation对一张图片进行变换&#xff0c;然后用SAM分割出其中的对象&#xff0c;因此可以找到一组图…

Session会话与请求域的区别

session会话和请求域&#xff08;也称为request域&#xff09;都是用于存储和管理用户特定信息的重要概念&#xff0c;但它们在作用范围和生命周期上有显著的不同。 请求域 (Request Domain) 作用范围&#xff1a;请求域是面向单次请求的。每次HTTP请求都会创建一个新的request…

【实战教程】如何使用JMeter来轻松测试WebSocket接口?

1、websocket接口原理 打开网页&#xff1a;从http协议&#xff0c;升级到websocket协议&#xff0c;请求建立websocket连接服务器返回建立成功成功客户端向服务端发送匹配请求服务端选择一个客服上线服务器返回客服id客户端向服务器发送消息服务器推送消息给指定的客服服务器…

EXCEL快速填充空白内容

** EXCEL快速填充空白内容 ** 1.全选所有需要填充的内容&#xff0c;按住电脑的F5或者CTRLG点击定位 2.可以看到空白处被自动选定&#xff0c;之后按电脑和⬆&#xff0c;最后CTRLenter 可以看到空白处已经被填充。

C#——this关键字详情

this关键字 在 C# 中&#xff0c;可以使用 this 关键字来表示当前对象&#xff0c;日常开发中我们可以使用 this 关键字来访问类中的成员属性以及函数。 使用this表示当前类的对象 执行结果 使用 this 关键字串联构造函数 执行结果 使用 this 关键字作为类的索引器 执行结果 …

02逻辑代数与硬件描述语言基础

2.1 逻辑代数&#xff08;简单逻辑的运算&#xff09; 2.2 逻辑函数的卡诺图&#xff08;从图论的角度&#xff09;化简法 2.3 硬件描述语言Verilog HDL基础&#xff08;研究生阶段才用得到&#xff09; 要求&#xff1a; 1、熟悉逻辑代数常用基本定律、恒等式和规则。 2、掌握…

蒸汽架空管道中的关键守护者:滑动管托、导向管托与固定管托

蒸汽架空管道中的关键守护者&#xff1a;滑动管托、导向管托、固定管托与补偿器的重要角色在蒸汽架空管道系统中&#xff0c;每一个组件都扮演着不可或缺的角色&#xff0c;共同确保管道的安全、高效运行。今天&#xff0c;我们就来深入探讨滑动管托、导向管托、固定管托以及补…

用一个实例看如何分享大量照片 续篇二,关于Exif (Exchangeable Image File) - 可交换图像文件

续篇二&#xff1a;说说关于照片隐含的 Exif (Exchangeable Image File) 可交换图像文件 数码照片的Exif 参数有很多&#xff0c;重要的Exif信息&#xff1a;拍摄日期、时间、拍摄器材、GPS信息。 当然这主要对自己的档案有意义&#xff0c;如果放到网上还是建议抹去这些信息。…

50etf期权合约一手多少钱你知道吗?

今天带你了解50etf期权合约一手多少钱你知道吗&#xff1f;50etf期权有不同价值的合约&#xff0c;每手50etf期权合约从几元到几百元再到上千元的都有&#xff0c;具体需要根据投资者选择了什么价值的合约。 50etf期权权利金 50ETF期权合约的权利金是买方需要缴纳的费用&…

Asm动态生成类和get and set方法

asm在解析文件的时候是按照特定顺序进行分析的&#xff0c;首先是visit方法&#xff0c;做类相关的解析&#xff0c;然后是注解&#xff0c;然后是属性&#xff0c;最后才是方法&#xff0c;属性是在所有方法分析前面进行&#xff0c;也就是只有当class文件中的所有属性都遍历完…