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

总体思路

在这里插入图片描述

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.lengthawait this.checkImageFiles(lists)// 添加水印,获得处理后的urlfor (const item of lists) {let waterUrl = await this.imageAddWatermarks(item.url);item.url = waterUrlitem.thumb = waterUrlthis.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.imgSrcconst fileId = imgInfo.fileIdlet item = this.baseInfoFormData.entrustFileList[fileListLen];this.baseInfoFormData.entrustFileList.splice(fileListLen,1,Object.assign(item, {message: "已上传",status: "success",url: imgSrc,thumb: imgSrc,}));this.baseInfoFormData.ahap7744 = fileIdfileListLen++;}} 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 urlctx.draw(false, () => {uni.canvasToTempFilePath({canvasId: "cid3",fileType: "jpg",success: (res) => {resolve(res.tempFilePath);},fail: (err) => {reject(err);},});});});
},

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

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

相关文章

容器和云原生(二):Docker容器化技术

目录 Docker容器的使用 Docker容器关键技术 Namespace Cgroups UnionFS Docker容器的使用 首先直观地了解docker如何安装使用&#xff0c;并快速启动mysql服务的&#xff0c;启动时候绑定主机上的3306端口&#xff0c;查找mysql容器的ip&#xff0c;使用mysql -h contain…

Cookie 和 Session 的工作流程

目录 一、Cookie是什么&#xff1f; 二、Session是什么? 三、Cookie的工作流程 四、Session的工作流程 五、Session和Cookie的区别和联系 一、Cookie是什么&#xff1f; Cookie是一种在网站和用户之间交换信息的机制。它是由Web服务器发送给用户浏览器的小型文本文件&#xff…

简述docker的网络模式

Docker 提供了多种网络模式&#xff0c;用于控制容器之间以及容器与主机之间的网络通信。以下是 Docker 的一些常见网络模式 briage模式&#xff1a; docker容器启动时默认就是该模式,在该模式下&#xff0c;docker容器会连接到一个名为docker0的虚拟以太网桥上&#xff0c;通…

数据可视化diff工具jsondiffpatch使用学习

1.jsondiffpatch 简介 jsondiffpatch 是一个用于比较和生成 JSON 数据差异的 JavaScript 库。它可以将两个 JSON 对象进行比较&#xff0c;并生成一个描述它们之间差异的 JSON 对象。这个差异对象可以用于多种用途&#xff0c;例如&#xff1a; 生成可视化的差异报告应用差异…

opencv-疲劳检测-眨眼检测

#导入工具包 from scipy.spatial import distance as dist from collections import OrderedDict import numpy as np import argparse import time import dlib import cv2FACIAL_LANDMARKS_68_IDXS OrderedDict([("mouth", (48, 68)),("right_eyebrow",…

matlab将数组值划分为两类

例如&#xff1a;大于0的处理为1&#xff0c;小于0的处理为-1. 当然&#xff0c;可以选择循环结构和选择结构&#xff0c;但是效率会很低。 这里直接使用逻辑语句完成。 % 不使用循环语句&#xff0c;将数组内值划分为两类 clc; clearvars; a[-0.1422 , -0.0433 , 0.1131 …

Leetcode每日一题:1267. 统计参与通信的服务器

原题 这里有一幅服务器分布图&#xff0c;服务器的位置标识在 m * n 的整数矩阵网格 grid 中&#xff0c;1 表示单元格上有服务器&#xff0c;0 表示没有。 如果两台服务器位于同一行或者同一列&#xff0c;我们就认为它们之间可以进行通信。 请你统计并返回能够与至少一台其…

基础论文学习(4)——CLIP

《Learning Transferable Visual Models From Natural Language Supervision》 CLIP的英文全称是Contrastive Language-Image Pre-training&#xff0c;即一种基于对比文本-图像对的预训练模型。CLIP是一种基于对比学习的多模态模型&#xff0c;与CV中的一些对比学习方法如moc…

【C语言练习】数组OJ题

目录 一.消失的数字思路1&#xff1a;思路2&#xff1a; 二.移除元素三.轮转数组四.删除有序数组中的重复项五.合并两个有序数组 一.消失的数字 题目&#xff1a; 思路1&#xff1a; 数组是从0加到N&#xff0c;所以把0到N的数加起来减去数组中的值&#xff0c;结果就是消失…

ssl卸载原理

SSL卸载&#xff0c;也称为SSL解密&#xff0c;是一种将SSL加密数据流卸成非加密的明文数据流的过程。SSL卸载通常在负载均衡器、代理服务器、WAF等设备中实现&#xff0c;可以提高传输效率和安全性。 SSL卸载的原理是将SSL数据流拦截下来&#xff0c;通过设备内置的证书进行解…

多维时序 | MATLAB实现SCNGO-CNN-Attention多变量时间序列预测

多维时序 | MATLAB实现SCNGO-CNN-Attention多变量时间序列预测 目录 多维时序 | MATLAB实现SCNGO-CNN-Attention多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.SCNGO-CNN-Attention超前24步多变量回归预测算法。 程序平台&#xff1a;无Attention适…

【MyBatis】:PageHelper分页插件与特殊字符处理

目录 一、PageHelper介绍 二、PageHelper使用 1. 导入pom依赖 2. Mybatis.cfg.xml 配置拦截器 3. 配置 Mapper.xml 4. 编写测试 三、特殊字符处理 1. 使用转义字符 2. 使用CDATA 区段 一、PageHelper介绍 PageHelper 是 Mybatis 的一个插件&#xff0c;这里就不扯了&a…

aardio的CS架构mysql数据表查询实例

import win.ui; /*DSG{{*/ var winform win.form(text"aardio form";right759;bottom479) winform.add( buttonAdd{cls"button";text"复制";left516;top442;right587;bottom473;z11}; buttonClose{cls"button";text"退出";…

多线程——学习记录2

目录 单例模式两种单例写法饿汉式和懒汉式的区别 RuntimeTimer 计时器两个线程间的通信关键点&#xff1a;wait()线程等待 和 notify()随机唤醒等待的线程; 三个或三个以上间的线程通信关键点&#xff1a;notifyAll()唤醒所有线程 线程间通信需要注意的问题JDK1.5的新特性互斥锁…

Java:ArrayList集合、LinkedList(链表)集合的底层原理及应用场景

ArrayList集合的底层原理及应用场景 LinkedList&#xff08;链表&#xff09;集合的底层原理及应用场景 单向链表 增加数据 删除数据 双向链表 LinkedList的应用场景之一:可以用来设计队列 入队 出队 LinkedList的应用场景之一:可以用来设计栈 压栈&#xff08;push),addFirst…

Socket基本原理

一、简单介绍 Socket&#xff0c;又称套接字&#xff0c;是Linux跨进程通信&#xff08;IPC&#xff0c;Inter Process Communication&#xff09;方式的一种。相比于其他IPC方式&#xff0c;Socket牛逼在于可做到同一台主机内跨进程通信&#xff0c;不同主机间的跨进程通信。…

redis常用五种数据类型详解

目录 前言&#xff1a; string 相关命令 内部编码 应用场景 hash 相关命令 内部编码 应用场景 list 相关命令 内部编码 应用场景 set 相关命令 内部编码 应用场景 Zset 相关命令 内部编码 应用场景 渐进式遍历 前言&#xff1a; redis有多种数据类型&…

Spring Clould 网关 - Gateway

视频地址&#xff1a;微服务&#xff08;SpringCloudRabbitMQDockerRedis搜索分布式&#xff09; Gateway网关-网关作用介绍&#xff08;P35&#xff09; Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2…

BTP Integration Suite学习笔记 - (Unit4) Developing with SAP Integration Suite

详细指导还是要看官方文档 4. 云集成管理 4.1 云集成介绍 什么是云集成&#xff1f; 前三章讲了很多内容&#xff0c;但都不是最核心的&#xff0c;通常我们用CPI是让他实现原来PI/PO的功能的&#xff0c;是用来做集成的。这章才刚开始。 云集成有以下几个特性&#xff1a;…

【LeetCode-中等题】438. 找到字符串中所有字母异位词

题目 题解一&#xff1a;暴力排序 依次截取三为排序好的字符串拿出来比较 // 方法一&#xff0c;暴力排序List<Integer> res new ArrayList<Integer>();int n s.length();int k p.length();if (n < k) {return res;}char[] chars p.toCharArray();Arrays.s…