uniapp 图片上传功能以及给图片添加水印

news/2024/12/23 4:59:42/

完整图片上传封装代码:

<template><view class="add-photo"><view class="tips" v-if="title"><text class="require" v-if="require">*</text><text>{{ title }}</text></view><van-uploader :file-list="fileList" :max-count="maxCount" :accept="accept" :capture="capture" :preview-image="true" @afterRead="afterReadHandle" @delete="deleteHandle" :sizeType="['compressed']"><slot name="default"></slot></van-uploader><!-- 获取有水印的图片过程 必须使canvas显示 获取完成后在隐藏掉canvas 配合canvas样式定位 使其错位 --><!-- canvas的隐藏 在小程序中 使用 v-if或display:none 都不生效   使用hidden属性 true隐藏 false显示 --><canvas :style="{ width: waterMarkParams.canvasWidth, height: waterMarkParams.canvasHeight }" canvas-id="myCanvas" :hidden="waterMarkParams.display"></canvas></view>
</template><script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
import { UserModule } from "@/store/modules/user";
import { uploadFile } from "@/utils/uni-api";
import APIConfig from "@/config";
import { generateWaterMarkText } from "@/utils/common";@Component
export default class UploadPhoto extends Vue {@Prop({ default: "", type: String }) private title?: string;@Prop({ default: false, type: Boolean }) private require?: boolean;@Prop({ default: () => [], type: Array }) private previewFileList!: any[];@Prop({ default: 99, type: Number }) private maxCount?: number;@Prop({ default: "image", type: String }) private accept?: string;@Prop({ default: ["album", "camera"], type: Array }) private capture?: Array<string>;@Prop({ default: false, type: Boolean }) private needDelConfirm?: boolean;@Prop({ default: null, type: Number }) private maxSize?: number; // kb@Prop({ default: null, type: Number }) private minSize?: number; // kb@Prop({ default: false, type: Boolean }) private needWatermark?: boolean;@Prop({ default: "", type: String }) private waterMarkText?: any;private fileList: any[] = [];// private token: string = `bearer ${UserModule.token}`;private files: any[] = [];private waterMarkParams = {display: false, // 控制 canvas 创建与销毁canvasWidth: "", // 默认宽度canvasHeight: "", // 默认高度contentHeight: 150, // 将要被绘制到图像中的矩形的高度(px)};@Watch("previewFileList", { immediate: true })private watchPreViewFileList(newVal: any[]): void {if (newVal.length > 0) {this.fileList = this.previewFileList.map((item) => ({path: item.prefix ? item.prefix + item.url : item.url,}));this.files = this.previewFileList;// console.log(this.fileList);}}private async afterReadHandle(e: any): Promise<void> {console.log("file", e);if (this.maxSize && this.minSize && e.detail.file) {const size = e.detail.file.size / 1024; // kbconst isLt2M = size >= this.minSize && size <= this.maxSize;if (!isLt2M) {uni.showToast({title: "图片文件大小需在20k-80k之间。",icon: "none",});return;}}const filePath = e.detail.file.path;uni.showLoading({title: "上传中...",});if (this.needWatermark) {this.addWatermark(filePath);} else {this.uploadFn(filePath);}}private deleteHandle(e: any): void {const self = this;if (this.needDelConfirm) {uni.showModal({content: "确认删除?",success(res) {if (res.confirm) {self.fileList.splice(e.detail.index, 1);self.files.splice(e.detail.index, 1);self.$emit("del-success", self.files);} else if (res.cancel) {}},});return;}this.fileList.splice(e.detail.index, 1);this.files.splice(e.detail.index, 1);this.$emit("del-success", this.files);}private async uploadFn(filePath: string) {const url = APIConfig[UserModule.type][process.env.NODE_ENV] + "/file/upload";try {let token = "";if (UserModule.type === UserModule.CORRECTION) {token = `${UserModule.correctionToken}`;} else {token = `bearer ${UserModule.token}`;}const result = await uploadFile(url, filePath, token);if (result.code === 0) {// 回显的数据this.fileList.push({url: result.data,});// 提交的文件数据 矫正对象端与工作人员端返回格式不一样if (UserModule.type === UserModule.CORRECTION) {this.files.push({url: result.data,});} else {this.files.push({prefix: result.data.prefix,url: result.data.url,title: result.data.title,type: 3,});}this.$emit("upload-success", this.files);} else {uni.showToast({icon: "none",title: "文件上传失败:" + result.msg,});this.$emit("upload-error", this.files);}} catch (error) {this.$emit("upload-error", error);}}private addWatermark(filePath: string) {console.log("filePath", filePath);this.waterMarkParams.display = false;const text = this.waterMarkText || generateWaterMarkText();return new Promise((resolve, reject) => {// 获取图片信息,配置 canvas 尺寸uni.getImageInfo({// 注意此时的地址是正常的图片地址 以下是给图片添加水印返回新的url地址src: filePath,success: (res) => {console.log("getImageInfo", res);const width = res.width < 600 ? res.width : 600;const height = res.height < 800 ? res.height : 800;this.waterMarkParams.canvasWidth = `${width}px`;this.waterMarkParams.canvasHeight = `${height}px`;// var ctx = uni.createCanvasContext('myCanvas');// 在自定义组件内 需要传递第二参数 this canvas才生效var ctx = uni.createCanvasContext("myCanvas", this);ctx.clearRect(0, 0, width, height);ctx.beginPath();ctx.drawImage(filePath, 0, 0, width, height); // 第一个参数是图片 第二、三是图片在画布位置 第四、五是将图片绘制成多大宽高(不写四五就是原图宽高)// 为图片添加水印ctx.translate(width / 2, height / 2);ctx.rotate((40 * Math.PI) / 180);//这部分是水印的大小位置和数量let fonstsize = 30;let horizontal = fonstsize * 20;let vertical = height / 4;for (let i = -1; i <= 1; i++) {for (let j = 0; j <= 4; j++) {ctx.beginPath();ctx.setFontSize(fonstsize);ctx.setFillStyle("#eee");// ctx.fillText("-仅供保险投保使用-", i * horizontal - res.width / 2, j * vertical - res.height / 2);/*** context.fillText(text,x,y,maxWidth);* text 规定在画布上输出的文本。* x 开始绘制文本的 x 坐标位置(相对于画布)。* y 开始绘制文本的 y 坐标位置(相对于画布)。* maxWidth 可选。允许的最大文本宽度,以像素计。*/ctx.fillText(text, i * horizontal - horizontal / 2, j * vertical - height / 2, horizontal);}}// 开始绘制添加水印的图片并显示在页面中ctx.draw(false, () => {setTimeout(() => {console.log("asdf");uni.canvasToTempFilePath({canvasId: "myCanvas",quality: 0.6, // 图片质量,范围0-1,1为最高质量width: width,height: height,destWidth: width,destHeight: height,success: (res) => {// 注意此时的地址是加了水印的图片地址(直接url输入浏览器也可以查看包含水印)console.log("canvasToTempFilePath", res.tempFilePath);this.waterMarkParams.display = true;this.uploadFn(res.tempFilePath);},fail: (err) => {uni.hideLoading();},},this);// 在自定义组件内 需要传递第二参数 this canvas才生效// }, this)}, 500);});},fail: (err) => {uni.hideLoading();},});});}
}
</script><style lang="scss" scoped>
.add-photo {padding: $medium-spacing-size;font-size: $small-text-size;background-color: $bg-color;.require {color: $danger-color;}.tips {margin-bottom: $large-spacing-size;}
}canvas {border: solid 1px gray;position: absolute;left: 5000upx;
}
</style>

给图片添加水印代码

<template><!-- 获取有水印的图片过程 必须使canvas显示 获取完成后在隐藏掉canvas 配合canvas样式定位 使其错位 --><!-- canvas的隐藏 在小程序中 使用 v-if或display:none 都不生效   使用hidden属性 true隐藏 false显示 --><canvas :style="{ width: waterMarkParams.canvasWidth, height: waterMarkParams.canvasHeight }" canvas-id="myCanvas" :hidden="waterMarkParams.display"></canvas>
</template><script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator";private waterMarkParams = {display: false, // 控制 canvas 创建与销毁canvasWidth: "", // 默认宽度canvasHeight: "", // 默认高度contentHeight: 150, // 将要被绘制到图像中的矩形的高度(px)
};// 传入图片路径
private addWatermark(filePath: string) {console.log("filePath", filePath);this.waterMarkParams.display = false;const text = this.waterMarkText || generateWaterMarkText();return new Promise((resolve, reject) => {// 获取图片信息,配置 canvas 尺寸uni.getImageInfo({// 注意此时的地址是正常的图片地址 以下是给图片添加水印返回新的url地址src: filePath,success: (res) => {console.log("getImageInfo", res);const width = res.width < 600 ? res.width : 600;const height = res.height < 800 ? res.height : 800;this.waterMarkParams.canvasWidth = `${width}px`;this.waterMarkParams.canvasHeight = `${height}px`;// var ctx = uni.createCanvasContext('myCanvas');// 在自定义组件内 需要传递第二参数 this canvas才生效var ctx = uni.createCanvasContext("myCanvas", this);ctx.clearRect(0, 0, width, height);ctx.beginPath();ctx.drawImage(filePath, 0, 0, width, height); // 第一个参数是图片 第二、三是图片在画布位置 第四、五是将图片绘制成多大宽高(不写四五就是原图宽高)// 为图片添加水印ctx.translate(width / 2, height / 2);ctx.rotate((40 * Math.PI) / 180);//这部分是水印的大小位置和数量let fonstsize = 30;let horizontal = fonstsize * 20;let vertical = height / 4;for (let i = -1; i <= 1; i++) {for (let j = 0; j <= 4; j++) {ctx.beginPath();ctx.setFontSize(fonstsize);ctx.setFillStyle("#eee");// ctx.fillText("-仅供保险投保使用-", i * horizontal - res.width / 2, j * vertical - res.height / 2);/*** context.fillText(text,x,y,maxWidth);* text 规定在画布上输出的文本。* x 开始绘制文本的 x 坐标位置(相对于画布)。* y 开始绘制文本的 y 坐标位置(相对于画布)。* maxWidth 可选。允许的最大文本宽度,以像素计。*/ctx.fillText(text, i * horizontal - horizontal / 2, j * vertical - height / 2, horizontal);}}// 开始绘制添加水印的图片并显示在页面中ctx.draw(false, () => {setTimeout(() => {console.log("asdf");uni.canvasToTempFilePath({canvasId: "myCanvas",quality: 0.6, // 图片质量,范围0-1,1为最高质量width: width,height: height,destWidth: width,destHeight: height,success: (res) => {// 注意此时的地址是加了水印的图片地址(直接url输入浏览器也可以查看包含水印)console.log("canvasToTempFilePath", res.tempFilePath);this.waterMarkParams.display = true;this.uploadFn(res.tempFilePath);},fail: (err) => {uni.hideLoading();},},this);// 在自定义组件内 需要传递第二参数 this canvas才生效// }, this)}, 500);});},fail: (err) => {uni.hideLoading();},});});
}
</script>

注意:如果图片不显示,可能是图片太大了,可以试着压缩一下图片大小

参考链接:https://blog.csdn.net/i_am_a_div/article/details/118302757


http://www.ppmy.cn/news/1557376.html

相关文章

【libuv】Fargo信令1:client发connect消息给到server

tcp 单机测试,进行模拟 (借助copilot实现) 【Fargo】28:字节序列client发connect消息给到serverserver 收到后回复ack给到客户端程序借助copilot实现。项目构建 Console依赖于Halo.dll提供的api,Halo 依赖于 Immanuel, 运行效果 遗留问题 客户端似乎么有逻辑收到ack做处理各…

数据结构_平衡二叉树

结点类 构造函数分为有参和无参&#xff0c;相同点都是初始化树高为1 class Node { public:int data; // 用于输出int val; // 数据域&#xff0c;用于排序int height; // 树高Node* left;Node* right;Node();Node(int v, int d);static int max(int a, int b); };Node::N…

姓名详批API接口_解析姓名构成与命理特征返回json数据

姓名详批 API 接口介绍 引言 姓名在中国文化中不仅是个人的代号&#xff0c;更承载着丰富的文化内涵和命理学意义。通过分析姓名的构成&#xff0c;可以揭示个人的性格特征、运势发展及潜在的命理影响。本文将介绍一个姓名详批的 API 接口&#xff0c;提供如何通过该接口获取…

【MFC】如何修改多文档视图的标签

新建工程同之前的几篇博客 新建一个调用菜单&#xff0c;并实现其内容 以下代码演示创建时设置标题&#xff0c;并保存到子框架中 #include "MFCApplication9Doc.h" #include "MFCApplication9View.h" void CMainFrame::On32771() {CMFCApplication9Doc*…

PC寄存器(Program Counter Register)jvm

在JVM(Java虚拟机)中,PC寄存器(Program Counter Register)扮演着至关重要的角色。以下是对JVM中PC寄存器的详细解释: 一、定义与功能 定义: JVM中的PC寄存器,也被称为程序计数器,是对物理PC寄存器的一种抽象模拟。它用于存储当前线程所执行的字节码指令的地址,即指…

.NET重点

B/S C/S什么语言 B/S&#xff1a; 浏览器端&#xff1a;JavaScript&#xff0c;HTML&#xff0c;CSS 服务器端&#xff1a;ASP&#xff08;.NET&#xff09;PHP/JSP 优势&#xff1a;维护方便&#xff0c;易于升级和扩展 劣势&#xff1a;服务器负担沉重 C/S java/.NET/…

node.js的简单示例

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;用于方便地构建快速、可扩展的网络应用。下面是一个简单的Node.js示例&#xff0c;它创建了一个简单的HTTP服务器&#xff0c;当访问服务器时&#xff0c;它会响应“Hello World” // 引入Node.js的HTTP模块…

源码分析之Openlayers中MousePosition鼠标位置控件

概述 本文主要介绍 Openlayers 中的MousePosition鼠标位置控件&#xff0c;该控件会创建一个元素在页面的右上方用来实时显示鼠标光标的位置坐标。该控件在实际应用很有效&#xff0c;可以实时获取鼠标位置&#xff0c;但是一般控件元素都会自定义。 源码分析 MousePosition…