完整图片上传封装代码:
<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