uniapp图片上传添加水印/压缩/剪裁

一、前言

最近遇到一个需求,微信小程序上传图片添加水印的需求,故此有该文章做总结, 功能涵盖定理地位,百度地址解析,图片四角水印,图片压缩,图片压缩并添加水印,图片剪裁,定位授权,保存图片到相册等

二、效果

三、代码实现核心

3.1)添加水印并压缩 核心实现

javascript 复制代码
// 添加水印并压缩
export function addWatermarkAndCompress(options, that, isCompress = false) {
	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) => {
									console.log('res.tempFilePath', 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,
								quality,
								fileType,
								success: (res) => {
									console.log('res.tempFilePath', res)
									resolve(res.tempFilePath)
								},
								fail() {
									reject(false)
								}
							}, that);
							// #endif 
						});
					})
				}
			});
		} else {
			const errStr = errLog.join(';')
			showMsg(errStr)
			reject(errStr)
		}
	})
}

3.2)剪切图片

javascript 复制代码
// 剪切图片
export function clipImg(options, that) {
	return new Promise((resolve, reject) => {
		const {
			errLog,
			config
		} = dealClipImgConfig(options)

		that.watermarkCanvasOption.width = 0
		that.watermarkCanvasOption.height = 0

		if (!errLog.length) {
			const {
				canvasId,
				imagePath,
				cWidth,
				cHeight,
				position
			} = config

			// 获取图片信息,以便获取图片的真实宽高信息
			uni.getImageInfo({
				src: imagePath,
				success: (info) => {
					const {
						width,
						height
					} = info; // 获取图片的原始宽高

					// 自定义剪裁范围要在图片内
					if (width >= cWidth && height >= cHeight) {

						that.watermarkCanvasOption.width = width
						that.watermarkCanvasOption.height = height
						that.$nextTick(() => {
							// 获取canvas绘图上下文
							const ctx = uni.createCanvasContext(canvasId, that);

							const {
								calcSX,
								calcSY,
								calcEX,
								calcEY
							} = calcClipPosition({
								cWidth,
								cHeight,
								position,
								width,
								height
							})

							// 绘制原始图片到canvas上
							ctx.drawImage(imagePath, 0, 0, width, height);

							// 绘制完成后执行的操作,这里不等待绘制完成就继续执行后续操作,因为我们要导出为图片
							ctx.draw(false, () => {
								// #ifndef MP-ALIPAY
								uni.canvasToTempFilePath({ // 将画布内容导出为图片
									canvasId,
									x: calcSX,
									y: calcSY,
									width: cWidth,
									height: cHeight,
									destWidth: cWidth,
									destHeight: cHeight,
									success: (res) => {
										console.log('res.tempFilePath',
											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) => {
										console.log('res.tempFilePath',
											res)
										resolve(res.tempFilePath)
									},
									fail() {
										reject(false)
									}
								}, that);
								// #endif 
							});
						})
					} else {
						return imagePath
					}
				}
			})

		} else {
			const errStr = errLog.join(';')
			showMsg(errStr)
			reject(errStr)
		}
	})
}

3.3)canvas画布标签

javascript 复制代码
		<!-- 给图片添加的标签 -->
		<canvas v-if="watermarkCanvasOption.width > 0 && watermarkCanvasOption.height > 0"
			:style="{ width: watermarkCanvasOption.width + 'px', height: watermarkCanvasOption.height + 'px' }"
			canvas-id="watermarkCanvas" id="watermarkCanvas" style="position: absolute; top: -10000000rpx;" />

以上代码具体的实现功能不做一一讲解,详细请看下方源码地址

四、源码地址

github: github.com/ArcherNull/...

五、总结

  • 图片的操作,例如压缩/剪裁/加水印都是需要借助canvas标签,也就是说需要有canvas实例通过该api实现这些操作
  • 当执行 ctx.drawImage(imagePath, 0, 0, width, height) 后,后续的操作的是对内存中的数据,而不是源文件

完结撒花,如果对您有帮助,请一键三连

相关推荐
小小小小宇几秒前
前端 异步任务并发控制
前端
bysking14 分钟前
【27-vue3】vue3版本的"指令式弹窗"逻辑函数createModal-bysking
前端·vue.js
LuckySusu15 分钟前
【HTML篇】script`标签中的 defer 与 async:深入解析异步加载 JavaScript 的差异
前端·html
CAD老兵15 分钟前
在 TypeScript 中复用已有 Interface 的部分属性:完整指南
前端
一头小鹿18 分钟前
【JS】原型和原型链 | 笔记整理
javascript
龚思凯20 分钟前
Vue 3 中 watch 监听引用类型的深度解析与全面实践
前端·vue.js
于冬恋30 分钟前
Web后端开发(请求、响应)
前端
red润36 分钟前
封装hook,复刻掘金社区,暗黑白天主题切换功能
前端·javascript·vue.js
Fly-ping37 分钟前
【前端】vue3性能优化方案
前端·性能优化
curdcv_po39 分钟前
前端开发必要会的,在线JS混淆加密
前端