uniapp调用u-upload上传图片压缩并添加水印

u-upload代码:

<u-upload
   :fileList="fileList1"
   @afterRead="afterRead"
   @delete="deletePic"
   name="1"
   :multiple="true"
   :maxCount="5"
>
</u-upload>
<!-- 给图片添加的标签 -->
<canvas
  :style="{
     width: watermarkCanvasOption.width + 'px',
     height: watermarkCanvasOption.height + 'px',
   }"
   canvas-id="waterMarkCanvas"
   id="waterMarkCanvas"
   style="position: absolute; top: -10000000rpx"
/>

定义数据:

fileList1:[],
watermarkCanvasOption: {
  width: 0,
  height: 0
},
pictureInfo: [], // 最后图片存储到这个数组中

上传图片方法:

async afterRead(event) {
      const that = this;
      //  当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
      const imgLists = [].concat(event.file);
      for (let i = 0; i < imgLists.length; i++) {
        const tPath = imgLists[i].url;
        if (tPath) {
         // addWatermark我是放到util.js文件里了,
         // addWatermark方法是精髓
          const res = await util.addWatermark(
            {
              canvasId: "waterMarkCanvas",
              imagePath: tPath,
              watermarkList: [
                {
                  fontSize: 40,
                  color: "#fff",
                  margin: 32,
                  position: "bottomLeft",
                  // 这里是水印信息,自己定义
                  text: ["2025-5-28 20:52:54", "深圳市"]
                }
              ]
            },
            that
          );
          imgLists[i].thumb = res;
          imgLists[i].url = res;
          // 下载
          // res && util.saveImageToPA(res)
        }
      }
      that.prepositionUploadFilePromise(event, imgLists);
    },
    prepositionUploadFilePromise(event, imgLists) {
      this.fileListLen = this[`fileList${event.name}`].length;
      imgLists.map((item) => {
        this[`fileList${event.name}`].push({
          ...item,
          status: "uploading",
          message: "上传中",
        });
      });
      for (let i = 0; i < imgLists.length; i++) {
        this.DKUploadFilePromise(imgLists, i, event);
      }
    },
    async DKUploadFilePromise(imgLists, i, event) {
      let res = await this.uploadFilePromise(imgLists[i].url);
      console.log('图片返回信息:', res)
      let item = this[`fileList${event.name}`][this.fileListLen];
      this[`fileList${event.name}`].splice(
        this.fileListLen,
        1,
        Object.assign(item, {
          status: "success",
          message: "",
        })
      );
    },
    uploadFilePromise(url) {
      return new Promise((resolve, reject) => {
        uni.uploadFile({
          url: "", // 上传图片地址
          filePath: url, // 图片URL
          name: "file",
          success: (res) => {
            this.pictureInfo.push(JSON.parse(res.data).url);
            setTimeout(() => {
              resolve(JSON.parse(res.data).url);
            }, 1000);
          },
        });
      });
    }

addWatermark方法:

// 添加水印并压缩
function addWatermark(options, that, isCompress = true) {
	return new Promise((resolve, reject) => {
		const {
			errLog,
			config
		} = dealWatermarkConfig(options)

		that.watermarkCanvasOption.width = 0
		that.watermarkCanvasOption.height = 0
		if (!errLog.length) {
			const {
				canvasId,
				imagePath,
				watermarkList,
				quality = 0.6
			} = config
			uni.getImageInfo({ // 获取图片信息,以便获取图片的真实宽高信息
				src: imagePath,
				success: (info) => {
					const {
						width: oWidth,
						height: oHeight,
						type,
						orientation
					} = info; // 获取图片的原始宽高
					const fileTypeObj = {
						'jpeg': 'jpg',
						'jpg': 'jpg',
						'png': 'png',
					}
					const fileType = fileTypeObj[type] || 'png'
					let width = oWidth
					let height = oHeight
					if (isCompress) {
						const {
							cWidth,
							cHeight
						} = calcRatioHeightAndWight({
							oWidth,
							oHeight,
							quality,
							orientation
						})
						// 按对折比例缩小
						width = cWidth
						height = cHeight
					}
					that.watermarkCanvasOption.width = width
					that.watermarkCanvasOption.height = height
					that.$nextTick(() => {
						// 获取canvas绘图上下文
						const ctx = uni.createCanvasContext(canvasId, that);
						// 绘制原始图片到canvas上
						ctx.drawImage(imagePath, 0, 0, width, height);
						// 绘制水印项
						const drawWMItem = (ctx, options) => {
							const {
								fontSize,
								color,
								text: cText,
								position,
								margin
							} = options
							// 添加水印
							ctx.setFontSize(fontSize); // 设置字体大小
							ctx.setFillStyle(color); // 设置字体颜色为红色

							if (isNotEmptyArr(cText)) {
								const text = cText.filter(Boolean)
								if (position.startsWith('bottom')) {
									text.reverse()
								}
								text.forEach((str, ind) => {
									const textMetrics = ctx.measureText(str);
									const {
										calcX,
										calcY
									} = calcPosition({
										height,
										width,
										position,
										margin,
										ind,
										fontSize,
										textMetrics
									})
									ctx.fillText(str, calcX, calcY, width);
								})
							} else {
								const textMetrics = ctx.measureText(cText);

								const {
									calcX,
									calcY
								} = calcPosition({
									height,
									width,
									position,
									margin,
									ind: 0,
									fontSize,
									textMetrics
								})
								// 在图片底部添加水印文字
								ctx.fillText(text, calcX, calcY, width);
							}
						}

						watermarkList.forEach(ele => {
							drawWMItem(ctx, ele)
						})

						// 绘制完成后执行的操作,这里不等待绘制完成就继续执行后续操作,因为我们要导出为图片
						ctx.draw(false, () => {
							// #ifndef MP-ALIPAY
							uni.canvasToTempFilePath({ // 将画布内容导出为图片
								canvasId,
								x: 0,
								y: 0,
								width,
								height,
								fileType,
								quality, // 图片的质量,目前仅对 jpg 有效。取值范围为 (0, 1],不在范围内时当作 1.0 处理。
								destWidth: width,
								destHeight: height,
								success: (res) => {
									resolve(res.tempFilePath)
								},
								fail() {
									reject(false)
								}
							}, that);
							// #endif

							// #ifdef MP-ALIPAY
							ctx.toTempFilePath({ // 将画布内容导出为图片
								canvasId,
								x: 0,
								y: 0,
								width: width,
								height: height,
								destWidth: width,
								destHeight: height,
								// fileType: 'png',
								success: (res) => {
									resolve(res.tempFilePath)
								},
								fail() {
									reject(false)
								}
							}, that);
							// #endif 
						});
					})
				}
			});
		} else {
			const errStr = errLog.join(';')
			showMsg(errStr)
			reject(errStr)
		}
	})
}
// 计算等比缩放的宽高
function calcRatioHeightAndWight(options) {
	const {
		oWidth,
		oHeight,
		quality,
		orientation
	} = options
    let cWidth = Math.floor(oWidth * quality)
    let cHeight = Math.floor(oHeight * quality)
	if (orientation === 'up') {
		return {
			cWidth,
			cHeight
		}
	} else {
		return {
			cWidth: cHeight,
			cHeight: cWidth
		}
	}
}
// 计算x,y位置
function calcPosition(options) {
	const {
		height,
		width,
		position,
		margin: marginVal,
		ind,
		fontSize,
		textMetrics
	} = options
	let calcX = marginVal
	let calcY = height - marginVal
	switch (position) {
		case 'topLeft': {
			calcX = marginVal
			calcY = marginVal + (fontSize * (ind + 1))
			break;
		}
		case 'topRight': {
			calcX = width - marginVal - textMetrics.width
			calcY = marginVal + (fontSize * (ind + 1))
			break;
		}

		case 'bottomLeft': {
			calcX = marginVal
			calcY = height - marginVal - (fontSize * ind)
			break;
		}

		case 'bottomRight': {
			calcX = width - marginVal - textMetrics.width
			calcY = height - marginVal - (fontSize * ind)
			break;
		}
	}

	return {
		calcX,
		calcY
	}
}
// 处理及校验传参
function dealWatermarkConfig(options) {
	const valdiateRulesObj = {
		canvasId: '画布id',
		imagePath: '本地图片路径',
	}

	const defaultWatermarkItem = {
		fontSize: 20,
		color: 'red',
		margin: 25,
		position: 'bottomLeft', // topLeft / topRight / bottomLeft / bottomRight
	}

	const errLog = validateObj(options, valdiateRulesObj)
	const {
		canvasId,
		watermarkList,
		...restObj
	} = options

	let nList = []
	console.log('watermarkListsdf', watermarkList)
	if (isNotEmptyArr(watermarkList)) {
		const nErrLog = []
		watermarkList.forEach(ele => {
			const nEle = {
				...defaultWatermarkItem,
				...ele,
			}

			if (convertNumber(nEle.fontSize) <= 16) {
				nErrLog.push('水印项字体大小需大于16')
			}
			
			if (convertNumber(nEle.margin) <= 10) {
				nErrLog.push('水印项边距大小需大于10')
			}

			if (typeof nEle.text === 'string') {
				EMPTY_STR_ARR.includes(nEle.text) && nErrLog.push('水印项文案不能为空')
			} else {
				!isNotEmptyArr(nEle.text) && nErrLog.push('水印项文案数组不能为空')
			}

			const positionArr = ['topLeft', 'topRight', 'bottomLeft', 'bottomRight']
			if (!positionArr.includes(nEle.position)) {
				nErrLog.push(`水印项位置不满足【${positionArr.join('/')}】其中之一`)
			}

			if (!nErrLog.length) {
				nList.push(nEle)
			}
		})
		errLog.push(...nErrLog)
	} else {
		errLog.push('水印项是必填的且为数组')
	}

	return {
		errLog,
		config: {
			...restObj,
			canvasId,
			watermarkList: nList
		}
	}
}
// 校验对象属性值不能为空
function validateObj(valdiateData, validateRules = {}, text = '') {
	if (isNotEmptyObj(validateRules)) {
		if (isNotEmptyObj(valdiateData)) {
			const errLog = []
			Object.entries(validateRules).forEach(item => {
				const [key, value] = item
				const val = valdiateData[key]
				if (typeof value === 'string') {
					if (EMPTY_STR_ARR.includes(val)) {
						errLog.push(`${text? text + '-' : ''}${validateRules[key]}不能为空`)
					}
				} else if (isNotEmptyObj(value)) {
					const {
						name,
						rules
					} = value
					if (isNotEmptyArr(rules) && !rules.includes(val)) {
						errLog.push(`${text? text + '-' : ''}${name}不能满足【${rules.join('/')}】其中之一`)
					}
				} else if (typeof value === 'function') {
					const fRes = value(val)
					fRes && errLog.push(fRes?.toString())
				}
			})
			return errLog
		} else {
			return []
		}
	} else {
		return []
	}
}
// 信息提示
export function showMsg(text, icon = 'none', duration = 2500) {
	uni.showToast({
		title: text,
		icon: icon,
		duration
	})
}
/**
 * @description: 转换为数字
 * @param {unknown} str
 * @return {*}
 */
export function convertNumber(str) {
	const val = Number(str)
	return isNaN(val) ? 0 : val
}
// 判断是否是空对象
export function isNotEmptyObj(obj) {
	return typeof obj === 'object' && Object.keys(obj)?.length
}

// 判断是否是空数组
export function isNotEmptyArr(arr) {
	return Array.isArray(arr) && arr.length
}
// 保存图片到相册
function saveImageToPA(tPath) {
	return new Promise((resolve, reject) => {
		if (tPath) {
			uni.saveImageToPhotosAlbum({
				filePath: tPath,
				success: function() {
					showMsg('保存成功')
					resolve(true)
				},
				fail() {
					reject(false)
				}
			});
		} else {
			showMsg('未获取到图片本地路径')
			reject(false)
		}
	})
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值