【uni-app】压缩图片并添加水印

news2024/9/25 15:28:55

总体思路

在这里插入图片描述

dom 结点

这里的 cvHeightcvWidth 初始时要设置为你后续需要压缩后的最大宽高。假设我们在图片上传后图片最大为 350 * 350

<u-upload :fileList="baseInfoFormData.entrustFileList" @afterRead="afterFileRead" multiple></u-upload>
<!-- 添加水印虚拟结点 -->
<view style="width: 0px; height: 0px; overflow: hidden">
	<canvas canvas-id="cid3" :style="{
        height: `${canvasSize.cvHeight}px`,
        width: `${canvasSize.cvWidth}px`,
      }"></canvas>
</view>

获取原本图片的宽高

这是一个用于获取图片尺寸的异步函数。它接受一个图片的URL作为参数,并返回一个Promise对象。
这个函数的作用是加载指定URL的图片,获取其宽度和高度,并将它们包装在一个对象中返回。

getImageSizeByUrl(url) {
  return new Promise((resolve, reject) => {
    // 创建一个新的Image对象,用于加载图片。
    const img = new Image();
    
    // 设置图片的src属性为传入的URL,以开始加载图片。
    img.src = url;
    
    // 当图片加载完成时,会触发onload事件。
    img.onload = function() {
      // 在加载成功时,通过resolve函数将图片的宽度和高度以对象的形式传递出去。
      resolve({
        width: img.width,
        height: img.height,
      });
    };
    
    // 当图片加载失败时,会触发onerror事件。
    img.onerror = function() {
      // 在加载失败时,通过reject函数返回一个带有错误信息的Error对象。
      reject(new Error("getImageSizeByUrl 加载图片失败"));
    };
  });
},

得到按比例压缩后的宽高

如果最长边大于350,计算缩放比例.如果最长边不大于350,直接返回原始尺寸。这里的350,就是最初始的 cvHeightcvWidth ,数值需要对应!

scaleImage(width, height) {
	return new Promise((resolve, reject) => {
		// 找到较长的一边
		const maxSide = Math.max(width, height);
		// 如果最长边大于350,计算缩放比例
		if (maxSide > 350) {
			const scale = 350 / maxSide;
			// 使用缩放比例来调整图片的长宽
			const newWidth = Math.round(width * scale);
			const newHeight = Math.round(height * scale);
			resolve({
				width: newWidth,
				height: newHeight,
			});
		} else {
			// 如果最长边不大于350,直接返回原始尺寸
			resolve({
				width,
				height,
			});
		}
	});
},

核心添加水印方法

这是一个用于在图片上添加水印并将结果绘制到 canvas 上的异步函数。
它接受一个图片的URL、图片的宽度和高度作为参数,并返回一个Promise对象。

imgToCanvas(url, width, height) {
  return new Promise((resolve, reject) => {
    // 使用 uni.createCanvasContext 创建一个 Canvas 2D 渲染上下文,参数为在 DOM 元素中定义的 canvas 元素的 id(cid3)。
    const ctx = uni.createCanvasContext("cid3");
    
    // 在 Canvas 上使用 drawImage 方法绘制图片,从指定的 URL 加载图片,并设置宽度和高度。
    ctx.drawImage(url, 0, 0, width, height);

    // 设置水印的大小和样式。
    ctx.setFontSize(width / 10); // 设置字体大小为图片宽度的十分之一。
    ctx.setFillStyle("rgba(150,150,150,0.2)"); // 设置水印的颜色和透明度。

    // 添加两行水印文字。
    // 第一行水印,居中显示在图片上半部分。
    ctx.fillText(
      "长沙市老年人居家",
      width / 2 - (width / 10) * 4,
      height / 2 - parseInt(width / 10 / 3) - width / 10 / 10 - 10
    );
    // 第二行水印,居中显示在图片下半部分。
    ctx.fillText(
      "适老化改造业务专用",
      width / 2 - (width / 10) * 4 - width / 10 / 2,
      height / 2 + parseInt((width / 10 / 3) * 2) + width / 10 / 10 + 10
    );

    // 使用 ctx.draw 方法将绘制的内容显示在 Canvas 上。
    // 第一个参数为 false,表示此次绘制不清空之前的内容。
    // 在绘制完成后,调用 uni.canvasToTempFilePath 将 Canvas 转换为临时文件路径。
    ctx.draw(false, () => {
      uni.canvasToTempFilePath({
        canvasId: "cid3", // 指定要转换的 Canvas 的 id。
        fileType: "jpg", // 指定要生成的图片文件类型。
        success: (res) => {
          // 在转换成功时,通过 resolve 函数返回生成的图片的临时文件路径。
          resolve(res.tempFilePath);
        },
        fail: (err) => {
          // 在转换失败时,通过 reject 函数返回错误信息。
          reject(err);
        },
      });
    });
  });
},

整合图片处理方法

async imageAddWatermarks(url) {
	try {
		const {
			width,
			height
		} = await this.getImageSizeByUrl(url);

		const scaledImage = await this.scaleImage(width, height);
		this.$set(this.canvasSize, "cvWidth", scaledImage.width);
		this.$set(this.canvasSize, "cvHeight", scaledImage.height);

		let waterUrl = await this.imgToCanvas(url, this.canvasSize.cvWidth, this.canvasSize.cvHeight);

		// 重置画布大小,否则会有显示不全的问题
		this.$set(this.canvasSize, "cvWidth", 350);
		this.$set(this.canvasSize, "cvHeight", 350);

		return waterUrl;
	} catch (error) {
		console.error(error);
	}
},

上传图片类型检测

这是一个用于检测上传的图片文件格式的函数。它接受一个文件名列表作为参数,并返回一个 Promise 对象。
函数的目的是检查上传的文件是否属于支持的图片格式(.jpg、.jpeg、.png)。

checkImageFiles(filenames) {
  // 定义支持的图片文件格式。
  const imageExtensions = ['.jpg', '.jpeg', '.png'];

  // 创建一个 Promise 数组,对每个文件进行格式检查。
  const promises = filenames.map(file => {
    return new Promise((resolve, reject) => {
      // 获取文件名的小写形式,并提取出文件扩展名。
      const fileExtension = file.name.toLowerCase().substring(file.name.lastIndexOf('.'));
      
      // 检查文件扩展名是否在支持的图片格式列表中。
      if (imageExtensions.includes(fileExtension)) {
        // 如果是支持的格式,通过 resolve 函数返回 true。
        resolve(true);
      } else {
        // 如果不是支持的格式,通过 reject 函数返回错误信息。
        reject('格式错误!请上传 jpg 或 png 图片');
      }
    });
  });

  // 使用 Promise.all 来等待所有的检查 Promise 完成。
  return Promise.all(promises);
},

参考代码

以下代码是配合 uView 框架实现的,用作参考即可

<u-upload :fileList="baseInfoFormData.entrustFileList" @afterRead="afterFileRead" @delete="deletePic" :name="entrustFile.name" multiple :maxCount="entrustFile.maxCount" :previewFullImage="true" width="163" height="102" :deletable="true">
	<view class="u-flex u-flex-center u-flex-items-center photo-con">
		<u-image width="18" height="18" src="@/static/applicant/upAdd.png"></u-image>
		<view class="up-txt">{{ entrustFile.upTxt }}</view>
	</view>
</u-upload>
<!-- 添加水印虚拟结点 -->
<view style="width: 0px; height: 0px; overflow: hidden">
	<canvas canvas-id="cid3" :style="{
        height: `${canvasSize.cvHeight}px`,
        width: `${canvasSize.cvWidth}px`,
      }"></canvas>
</view>
async afterFileRead(event) {
	try {
		let lists = [].concat(event.file)
		let fileListLen = this.baseInfoFormData.entrustFileList.length
		await this.checkImageFiles(lists)
		// 添加水印,获得处理后的url
		for (const item of lists) {
			let waterUrl = await this.imageAddWatermarks(item.url);
			item.url = waterUrl
			item.thumb = waterUrl
			this.baseInfoFormData.entrustFileList.push({
				...item,
				status: 'uploading',
				message: '上传中'
			});
		}
		// 将添加完水印的图片上传至统一存储
		for (let i = 0; i < lists.length; i++) {
			const imgInfo = await this.uploadFilePromise(lists[i].url);
			const imgSrc = imgInfo.imgSrc
			const fileId = imgInfo.fileId
			let item = this.baseInfoFormData.entrustFileList[fileListLen];
			this.baseInfoFormData.entrustFileList.splice(
				fileListLen,
				1,
				Object.assign(item, {
					message: "已上传",
					status: "success",
					url: imgSrc,
					thumb: imgSrc,
				})
			);
			this.baseInfoFormData.ahap7744 = fileId
			fileListLen++;
		}
	} catch (e) {
		uni.$u.toast(e)
	}
},
uploadFilePromise(url) {
	return new Promise((resolve, reject) => {
		uni.uploadFile({
			url: config.basePath + config.interfaceConfig["uploadSLHFile"],
			filePath: url,
			name: "file",
			formData: {
				azzx0025: this.getWxuuid(),
				businessLevel: 1,
				azzx0040: "slh",
				azzx0063: "01",
			},
			success: (res) => {
				const result = JSON.parse(res.data);
				const fileId = result.data.resultData.fileId;
				const imgSrc = this.$url_config.fileBasePath + fileId;
				const imgInfo = {
					fileId,
					imgSrc
				}
				resolve(imgInfo);
			},
		});
	});
},
async imageAddWatermarks(url) {
	try {
		const {
			width,
			height
		} = await this.getImageSizeByUrl(url);

		const scaledImage = await this.scaleImage(width, height);
		this.$set(this.canvasSize, "cvWidth", scaledImage.width);
		this.$set(this.canvasSize, "cvHeight", scaledImage.height);

		let waterUrl = await this.imgToCanvas(url, this.canvasSize.cvWidth, this.canvasSize.cvHeight);

		// 重置画布大小,否则会有显示不全的问题
		this.$set(this.canvasSize, "cvWidth", 350);
		this.$set(this.canvasSize, "cvHeight", 350);

		return waterUrl;
	} catch (error) {
		console.error(error);
	}
},
getImageSizeByUrl(url) {
	return new Promise((resolve, reject) => {
		const img = new Image();
		img.src = url;
		img.onload = function() {
			resolve({
				width: img.width,
				height: img.height,
			});
		};
		img.onerror = function() {
			reject(new Error("getImageSizeByUrl 加载图片失败"));
		};
	});
},
// 如果最长边大于350,计算缩放比例.如果最长边不大于350,直接返回原始尺寸
scaleImage(width, height) {
	return new Promise((resolve, reject) => {
		// 找到较长的一边
		const maxSide = Math.max(width, height);
		// 如果最长边大于350,计算缩放比例
		if (maxSide > 350) {
			const scale = 350 / maxSide;
			// 使用缩放比例来调整图片的长宽
			const newWidth = Math.round(width * scale);
			const newHeight = Math.round(height * scale);
			resolve({
				width: newWidth,
				height: newHeight,
			});
		} else {
			// 如果最长边不大于350,直接返回原始尺寸
			resolve({
				width,
				height,
			});
		}
	});
},
imgToCanvas(url, width, height) {
	return new Promise((resolve, reject) => {
		const ctx = uni.createCanvasContext("cid3"); //在dom元素中定义了一个不显示的canvas元素
		ctx.drawImage(url, 0, 0, width, height);

		// 设置水印的大小,位置
		ctx.setFontSize(width / 10);
		ctx.setFillStyle("rgba(150,150,150,0.2)");

		// 添加水印
		ctx.fillText(
			"水印具体内容1",
			width / 2 - (width / 10) * 4,
			height / 2 - parseInt(width / 10 / 3) - width / 10 / 10 - 10
		);
		ctx.fillText(
			"水印具体内容2",
			width / 2 - (width / 10) * 4 - width / 10 / 2,
			height / 2 + parseInt((width / 10 / 3) * 2) + width / 10 / 10 + 10
		);

		// 绘制到canvas上,返回加完水印的 base64 url
		ctx.draw(false, () => {
			uni.canvasToTempFilePath({
				canvasId: "cid3",
				fileType: "jpg",
				success: (res) => {
					resolve(res.tempFilePath);
				},
				fail: (err) => {
					reject(err);
				},
			});
		});
	});
},

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

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

相关文章

2023前端求职经历回顾及面试题总结

文章目录 前言一、求职经历二、前端面经1. 投简历的正确打开方式2. 面经及面试题复盘外企面经、面试题大厂面经、面试题面试中的笔试有三种笔试题 给同行的温馨提示我的其他热门文章 前言 2023 年的春节之前&#xff0c;几乎没有公司招人&#xff0c;直到 2023年2月10日 左右&a…

科技资讯|苹果Apple Watch新专利,可根据服装、表带更换表盘颜色

根据美国商标和专利局&#xff08;USPTO&#xff09;公示的清单&#xff0c;苹果公司近日获得了一项 Apple Watch 相关的技术专利&#xff0c;最大的亮点在于配备颜色采样传感器&#xff0c;可以根据表带、服装自动变幻变盘颜色和主题。 Apple Watch 正面配备颜色采样传感器&am…

Python快速入门体验

Python快速入门体验 一、环境信息1.1 硬件信息1.2 软件信息 二、Conda安装2.1 Conda介绍2.1.1 Conda简介2.1.2 Conda、Anaconda及Miniconda及的关系 2.2 Conda安装包下载2.2.1 Miniconda下载2.2.2 Anconda下载 2.3 Conda安装2.3.1 Miniconda安装2.3.2 Anconda安装 2.4 Conda初始…

微信小程序的springboot实现 个人行程日程安排系统

本站后台采用Java的springboot框架进行后台管理开发&#xff0c;可以在浏览器上登录进行后台数据方面的管理&#xff0c;MySQL作为本地数据库&#xff0c;微信小程序用到了微信开发者工具&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特…

openCV实战-系列教程5:边缘检测(Canny边缘检测/高斯滤波器/Sobel算子/非极大值抑制/线性插值法/梯度方向/双阈值检测 )、原理解析、源码解读

1、Canny边缘检测流程 Canny是一个科学家在1986年写了一篇论文&#xff0c;所以用自己的名字来命名这个检测算法&#xff0c;Canny边缘检测算法这里写了5步流程&#xff0c;会用到之前《openCV实战-系列教程》的内容。 使用高斯滤波器&#xff0c;以平滑图像&#xff0c;滤除…

揭开Android系统启动的神秘面纱

当有人问我们android中app启动流程的时候&#xff0c;我们总是会提到zygote。but&#xff0c;zygote又是从何而来&#xff1f;由此问题我想到了android系统的启动流程&#xff0c;zygote肯定是在系统初始化时创建的一个进程。带着这个疑问我去查询了一些android系统启动流程的资…

Linux配置nginx反向代理

在云服务器上部署高并发的服务&#xff0c;使用Nginx作为反向代理是一种常见的做法&#xff0c;可以实现流量分发、负载均衡&#xff0c;同时提升系统的可靠性和性能。 步骤概览&#xff1a; 安装Nginx&#xff1a; 确保服务器已安装Nginx。若未安装&#xff0c;可使用适用于你…

【c语言】文件操作 万字详解

目录 一&#xff0c;为什么使用文件 二&#xff0c;什么是文件 1&#xff0c;程序文件 2&#xff0c;数据文件 3&#xff0c;文件名 三&#xff0c;文件的打开和关闭 1&#xff0c;文件指针 2&#xff0c;文件的打开和关闭 四&#xff0c; 文件的顺序读写 1&#xff0c;顺序…

机器人制作开源方案 | 桌面级机械臂--本体说明+驱动及控制

一、本体说明 1. 机械臂整体描述 该桌面级机械臂为模块化设计&#xff0c;包含主机模块1个、转台模块1个、二级摆动模块1个、可编程示教盒1个、2种末端执行器、高清摄像头&#xff0c;以及适配器、组装工具、备用零件等。可将模块快速组合为一个带被动关节的串联3自由度机械臂…

用Python搭建个让你呼吸顺畅-ChatGPT

目录 ChatGPT使用时可能会遇到 1.请待命&#xff0c;我们正在检查您的浏览器... 2. 访问被拒绝。抱歉&#xff0c;您已被阻止 3. ChatGPT 目前已满负荷运转 4. 此内容可能违反我们的内容政策。 5.出了点问题。 6. 蹦字慢吞吞&#xff0c;卡顿不流畅&#xff0c;不知道的…

对称输出字符串(对称+递归)--夏令营

题目 tips&#xff1a; 1、巧思&#xff1a;类似于将输入的字符串按一个上一个下输出&#xff1b;所以可以用递归栈思考&#xff0c;第一个输入的直接输出&#xff0c;第二个存下来存进栈&#xff0c;如果没有字符串了&#xff0c;就可以输出第二个字符串&#xff0c;如果还有…

docker for window更改到非系统盘的使用记录

1、使用Hyper-v模式的docker安装 2、安装docker for windows后安装目录没办法自己选择&#xff0c;固定在c盘 卸载后通过命令行方式设置软连接方式后重新安装来让其安装到软连接的d盘&#xff0c;解决c盘空间问题 mklink /j "C:\Program Files\Docker" "D:\Pr…

制造执行系统(MES)在家具行业中的应用

制造执行系统&#xff08;MES&#xff09;在家具行业中有许多应用&#xff0c;它可以帮助家具制造商提高生产效率、质量控制和整体管理。以下是MES系统在家具行业中的一些应用领域&#xff1a; 1. 生产计划与调度&#xff1a;MES可以帮助家具制造商优化生产计划和调度&#xff…

php thinkphp 抖音支付,订单同步接口分享

1. 抖音支付 需要获取抖音小程序的AppID,AppSecret,需要配置回调地址&#xff0c;Token获取SALT 官方地址&#xff1a;支付&#xff0c;订单同步 以下干货仅针对于有一定开发基础的精英&#xff0c;0基础的止步。 public function DouyinPay($openId,$id,$body 抖音担保支付…

微服务中间件--分布式搜索ES

分布式搜索ES 11.分布式搜索 ESa.介绍ESb.IK分词器c.索引库操作 (类似于MYSQL的Table)d.查看、删除、修改 索引库e.文档操作 (类似MYSQL的数据)1) 添加文档2) 查看文档3) 删除文档4) 修改文档 f.RestClient操作索引库1) 创建索引库2) 删除索引库/判断索引库 g.RestClient操作文…

用户端Web自动化测试_L3

目录&#xff1a; 浏览器复用Cookie 复用pageobject设计模式异常自动截图测试用例流程设计电子商务产品实战 1.浏览器复用 复用浏览器简介 为什么要学习复用浏览器&#xff1f; 自动化测试过程中&#xff0c;存在人为介入场景提高调试UI自动化测试脚本效率 复用已有浏览…

13. Docker实战之安装MySQL

目录 1、前言 2、部署MySQL 2.1、Docker仓库查看镜像 2.2、拉取MySQL镜像 2.3、创建持久化目录 2.4、启动MySQL容器 2.5、查看宿主机上的MySQL目录 2.6、本地MySQL测试 2.7、新建MySQL用户&#xff0c;配置远程访问 2.8、本地Navicat连接测试 3、为什么数据库不适合D…

长胜证券:a股交易时间是几点到几点?

股票商场是一个高速工作的场所&#xff0c;关于新手出资者来说&#xff0c;他们可能不知道A股买卖的时刻是什么时分开始和完毕&#xff0c;这将给他们在买卖过程中带来一些麻烦。本文将从不同的角度来分析A股买卖时刻&#xff0c;帮助读者更好地了解A股买卖时刻的相关规定。 A股…

Talk | 香港中文大学张懿元:由MetaTransformer探索统一的多模态学习

本期为TechBeat人工智能社区第524期线上Talk&#xff01; 北京时间8月23日(周三)20:00&#xff0c;香港中文大学博士生—张懿元的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “探索模态通用感知”&#xff0c;介绍了多模态学习和发展统一的多模态…

espidf vscode 安装出错ERROR_INVALID_PIP

解决链接&#xff1a;https://www.cnblogs.com/xiaohuzaixue/p/17558731.html 注意 不要使用win11的右键打开终端&#xff0c;在文件管理器上方输入cmd打开终端才有用。