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