uni-app微信小程序-利用canvas给图片添加水印

实现思路

选择图片 → 将图片绘制到 canvas 中并绘制水印 →将添加水印的图片绘制到 canvas 中 → 将 canvas 画布转换为图片地址 → 上传/展示操作

一、选择图片

注意:微信小程序在选择照片或者唤起相机之前需要获取相应的 权限

  1. 利用 uni.getSetting 查看用户是否调用相机的权限(有就选择图片,没有就获取授权后再选择图片)
    uni.getSetting({
    	success: async (res) => {
    		// 是否有相机权限
    		if (!res.authSetting['scope.camera']) {
    			// 获取相机权限
    			uni.authorize({
    				scope: 'scope.camera',
    				async success() {
    					// 选择图片
    					chooseImage()
    				}
    			})
    		} else {
    			// 选择图片
    			chooseImage()
    		}
    	},
    	fail(err) {
    		console.log('err :>> ', err);
    	}
    })
    
  2. 利用 uni.chooseMedia 选择图片
    uni.chooseMedia({
    	count: 1, // 1就是最多选择一张图片
    	mediaType: ['image'],
    	sourceType: _this.sourceType, // album相册、camera相机
    	success: async (res) => {
    		const tempFilePath = res.tempFiles[0].tempFilePath
    		//添加水印
    		_this.addWaterMarking(tempFilePath)
    	}
    })
    

二、将图片绘制到 canvas 中并绘制水印

  1. 放置 canvas 画布并将其放置到窗口外

    <template>
    	<view class='waterMarkingCamera'>
    		<canvas id="myCanvas" type="2d" :style="'position:fixed;left:8888px'" />
    	</view>
    </template>
    
  2. 利用 uni.getImageInfo 获取原图片信息

    addWaterMarking(imageUrl) {
    	//获取原图片信息
    	uni.getImageInfo({
    		src: imageUrl,
    		success: async function(res) {
    			const imgWidth = res.width; // 图片的宽
    			const imgHeight = res.height; // 图片的高
    		}
    	})
    }
    
  3. 利用 wx.createSelectorQuery 获取 canvas 对象并渲染上下文

    addWaterMarking(imageUrl) {
    	//获取原图片信息
    	uni.getImageInfo({
    		src: imageUrl,
    		success: async function(res) {
    			const imgWidth = res.width; // 图片的宽
    			const imgHeight = res.height; // 图片的高
    			// 获取 canvas 
    			wx.createSelectorQuery()
    				.in(_this)
    				.select('#myCanvas') // <canvas id="myCanvas" type="2d"  :style="'position:fixed;left:8888px'" />
    				.fields({
    					node: true,
    					size: true
    				})
    				.exec((resCanvas) => {
    					// canvas 对象
    					const canvas = resCanvas[0].node
    					// 初始化画布大小
    					canvas.width = imgWidth;
    					canvas.height = imgHeight;
    					// 渲染上下文
    					const ctx = canvas.getContext('2d')
    				}
    		}
    	})
    }
    
  4. 创建图片对象并绘制图片和水印

    addWaterMarking(imageUrl) {
    	//获取原图片信息
    	uni.getImageInfo({
    		src: imageUrl,
    		success: async function(res) {
    			const imgWidth = res.width; // 图片的宽
    			const imgHeight = res.height; // 图片的高
    			// 获取 canvas 
    			wx.createSelectorQuery()
    				.in(_this)
    				.select('#myCanvas') // <canvas id="myCanvas" type="2d"  :style="'position:fixed;left:8888px'" />
    				.fields({
    					node: true,
    					size: true
    				})
    				.exec((resCanvas) => {
    					// canvas 对象
    					const canvas = resCanvas[0].node
    					// 初始化画布大小
    					canvas.width = imgWidth;
    					canvas.height = imgHeight;
    					// 渲染上下文
    					const ctx = canvas.getContext('2d')
    					// 图片对象
    					const image = canvas.createImage()
    					// 图片加载完成回调
    					image.onload = async () => {
    						let waterMarkingText = _this.waterMarkingText;
    						// 将图片绘制到 canvas 上
    						ctx.drawImage(image, 0, 0, imgWidth, imgHeight)
    						// 添加遮罩水印
    						if (_this.isOverlay) {
    							_this.addOverlay(ctx, res, waterMarkingText)
    						}
    						// 添加文字水印
    						if (typeDetection.call(waterMarkingText) == '[object Array]') {// 判断是否添加多条水印
    							for (let i = 0; i < waterMarkingText.length; i++) {
    								// 设置水印内容
    								const text = waterMarkingText[i] == 'time' ? 
    								_this.$parseTime(new Date()) :
    								waterMarkingText[i] == 'address' ?
    								_this.address : waterMarkingText[i]
    								// 设置水印颜色
    								ctx.fillStyle = typeDetection.call(_this.fontColor) == '[object Array]' ?
    								_this.fontColor[i] : _this.fontColor;
    								// 设置水印文字大小
    								ctx.font = `${typeDetection.call(_this.fontSize) == '[object Array]' ?
    								_this.fontSize[i] : _this.fontSize}px arial`;
    								// 绘制水印文字
    								ctx.fillText(text, _this.interval[0], res.height - 
    								(typeDetection.call(_this.fontSize) == '[object Array]' ?
    								 _this.fontSize[i] : 
    								_this.fontSize) - _this.interval[1] * i)
    								ctx.restore();
    							}
    						} else {
    							// 设置水印颜色
    							ctx.fillStyle = _this.fontColor;
    							// 设置水印文字大小
    							ctx.font = `${_this.fontSize}px arial`;
    							// 绘制水印文字
    							ctx.fillText(waterMarkingText, _this.interval[0], res.height -  _this.fontSize);
    							ctx.restore();
    						}
    					}
    					image.src = imageUrl
    				})
    		}
    	})
    }
    

三、将 canvas 画布转换为图片地址

	addWaterMarking(imageUrl) {
		//获取原图片信息
		uni.getImageInfo({
			src: imageUrl,
			success: async function(res) {
				const imgWidth = res.width; // 图片的宽
				const imgHeight = res.height; // 图片的高
				// 获取 canvas 
				wx.createSelectorQuery()
					.in(_this)
					.select('#myCanvas') // <canvas id="myCanvas" type="2d"  :style="'position:fixed;left:8888px'" />
					.fields({
						node: true,
						size: true
					})
					.exec((resCanvas) => {
						// canvas 对象
						const canvas = resCanvas[0].node
						// 初始化画布大小
						canvas.width = imgWidth;
						canvas.height = imgHeight;
						// 渲染上下文
						const ctx = canvas.getContext('2d')
						// 图片对象
						const image = canvas.createImage()
						// 图片加载完成回调
						image.onload = async () => {
							let waterMarkingText = _this.waterMarkingText;
							// 将图片绘制到 canvas 上
							ctx.drawImage(image, 0, 0, imgWidth, imgHeight)
							// 添加遮罩水印
							if (_this.isOverlay) {
								_this.addOverlay(ctx, res, waterMarkingText)
							}
							// 添加文字水印
							if (typeDetection.call(waterMarkingText) == '[object Array]') {
								for (let i = 0; i < waterMarkingText.length; i++) {
									// 设置水印内容
									const text = waterMarkingText[i] == 'time' ? 
									_this.$parseTime(new Date()) :
									waterMarkingText[i] == 'address' ?
									_this.address : waterMarkingText[i]
									// 设置水印颜色
									ctx.fillStyle = typeDetection.call(_this.fontColor) == '[object Array]' ?
									_this.fontColor[i] : _this.fontColor;
									// 设置水印文字大小
									ctx.font = `${typeDetection.call(_this.fontSize) == '[object Array]' ?
									_this.fontSize[i] : _this.fontSize}px arial`;
									// 绘制水印文字
									ctx.fillText(text, _this.interval[0], res.height - 
									(typeDetection.call(_this.fontSize) == '[object Array]' ?
									 _this.fontSize[i] : 
									_this.fontSize) - _this.interval[1] * i)
									ctx.restore();
								}
							} else {
								// 设置水印颜色
								ctx.fillStyle = _this.fontColor;
								// 设置水印文字大小
								ctx.font = `${_this.fontSize}px arial`;
								// 绘制水印文字
								ctx.fillText(waterMarkingText, _this.interval[0], res.height -  _this.fontSize);
								ctx.restore();
							}
							// 某些平台 canvas 绘制比较慢,需要等待绘制完成
							await _this.sleep(500)
							// 将 canvas 画布转换为图片地址
							wx.canvasToTempFilePath({
								canvas: canvas,
								async success(res) {
								// 上传图片操作
								await _this.uploadImage(res.tempFilePath)
								}
							})
						}
						image.src = imageUrl
					})
			}
		})
	}

四、最终效果

在这里插入图片描述

五、完整代码

<template>
	<view class='waterMarkingCamera'>
		<canvas id="myCanvas" type="2d" :style="'position:fixed;left:8888px'" />
		<view class="waterMarkingCamera-icon" @tap='handleChooseImage()' v-show="!imageArr||imageArr.length<limit">
			<slot name="W_M_C_Icon">
				<text class="iconfont icon-a-xiangji"></text>
			</slot>
		</view>
	</view>
</template>
<script>
	var typeDetection = Object.prototype.toString;
	export default {
		name: 'waterMarkingCamera',
		/**	
		 *	waterMarkingCamera 水印相机
		 *	@property {Array} attachmentList 附件列表
		 * 	@property {Array} sourceType 数据源类型 album(相册) camera(相机)
		 * 	@property {Number|String} limit 限制照片数 999
		 * 	@property {Boolean} isOverlay 是否需要遮罩 true
		 * 	@property {Array|String} waterMarkingText 水印文字 ['time','address'] ['水印文字',水印文字2] | '水印文字' time(添加时间)、address(添加地址)
		 * 	@property {Array|String} fontSize 水印文字大小 32 ['32',100] | '32'
		 * 	@property {Array|String} fontColor 水印文字颜色 '#ffffff' ['#ffffff','red','rgba(255, 255, 255,0.2)','rgb(255, 255, 255)'] | '#ffffff'
		 * 	@property {Array} interval 间距 [50, 80]
		 * 
		 * */
		props: {
			attachmentList: {
				type: Array,
				default: () => {
					return []
				}
			},
			sourceType: {
				type: Array,
				default: () => {
					return ['album', 'camera']
				}
			},
			limit: {
				type: [Number, String],
				default: 999
			},
			isOverlay: {
				type: Boolean,
				default: true
			},
			waterMarkingText: {
				type: [Array, String],
				default: () => {
					return ['time', 'address']
				}
			},
			fontSize: {
				type: [Array, Number, String],
				default: 32
			},
			fontColor: {
				type: [Array, String],
				default: '#ffffff'
			},
			interval: {
				type: Array,
				default: () => {
					return [50, 50]
				}
			}
		},
		data() {
			return {
				imageArr: [],
				address: '你的位置信息'
			}
		},
		mounted() {
			this.imageArr = this.attachmentList
			// 反转内容数组
			if (typeDetection.call(this.waterMarkingText) ==
				'[object Array]') {
				this.waterMarkingText.reverse()
			}
		},
		methods: {
			// 选择照片
			handleChooseImage() {
				let _this = this
				uni.getSetting({
					success: async (res) => {
						const chooseImage = () => {
							uni.chooseMedia({
								count: 1, // 最多选择一张图片
								mediaType: ['image'],
								sourceType: _this.sourceType, // 相册、相机
								success: async (res) => {
									const tempFilePath = res.tempFiles[0].tempFilePath
									_this.addWaterMarking(tempFilePath)
								}
							})
						}
						// 是否有相机权限
						if (!res.authSetting['scope.camera']) {
							// 获取相机权限
							uni.authorize({
								scope: 'scope.camera',
								async success() {
									// 选择照片
									chooseImage()
								}
							})
						} else {
							// 选择照片
							chooseImage()
						}
					},
					fail(err) {
						console.log('err :>> ', err);
					}
				})
			},
			// 添加水印
			addWaterMarking(imageUrl) {
				uni.showLoading({
					title: '上传中...',
					mask: true
				})
				var _this = this;
				//获取原图片信息
				uni.getImageInfo({
					src: imageUrl,
					success: async function(res) {
						const imgWidth = res.width; // 图片的宽
						const imgHeight = res.height; // 图片的高
						// 获取canvas 
						wx.createSelectorQuery()
							.in(_this)
							.select('#myCanvas') // 在 WXML 中填入的 id
							.fields({
								node: true,
								size: true
							})
							.exec((resCanvas) => {
								// Canvas 对象
								const canvas = resCanvas[0].node
								// 初始化画布大小
								canvas.width = imgWidth;
								canvas.height = imgHeight;
								// 渲染上下文
								const ctx = canvas.getContext('2d')
								// 图片对象
								const image = canvas.createImage()
								// 图片加载完成回调
								image.onload = async () => {
									let waterMarkingText = _this.waterMarkingText;
									// 将图片绘制到 canvas 上
									ctx.drawImage(image, 0, 0, imgWidth, imgHeight)
									// 添加遮罩水印
									if (_this.isOverlay) {
										_this.addOverlay(ctx, res, waterMarkingText)
									}
									// 添加文字水印
									if (typeDetection.call(waterMarkingText) ==
										'[object Array]') {
										for (let i = 0; i < waterMarkingText.length; i++) {
											// 设置水印内容
											const text = waterMarkingText[i] == 'time' ?
												_this.$parseTime(new Date()) :
												waterMarkingText[i] ==
												'address' ?
												_this.address : waterMarkingText[i]
											// 设置水印颜色
											ctx.fillStyle = typeDetection.call(_this.fontColor) ==
												'[object Array]' ?
												_this.fontColor[i] : _this.fontColor;
											// 设置水印文字大小
											ctx.font = `${typeDetection.call(_this.fontSize) ==
												'[object Array]' ?
												_this.fontSize[i] : _this.fontSize}px arial`;
											// 绘制水印文字
											ctx.fillText(text, _this.interval[0], res.height - (
													typeDetection
													.call(_this
														.fontSize) == '[object Array]' ?
													_this.fontSize[i] : _this.fontSize) - _this
												.interval[1] * i)
											ctx.restore();
										}
									} else {
										// 设置水印颜色
										ctx.fillStyle = _this.fontColor;
										// 设置水印文字大小
										ctx.font = `${_this.fontSize}px arial`;
										// 绘制水印文字
										ctx.fillText(waterMarkingText, _this.interval[0], res
											.height -
											_this
											.fontSize);
										ctx.restore();
									}
									// 某些平台 canvas 绘制比较慢,需要等待绘制完成
									await _this.sleep(500)
									// 将 canvas 画布转换为图片地址
									wx.canvasToTempFilePath({
										canvas: canvas,
										async success(res) {
											// 上传图片操作
										}
									})
								}
								image.src = imageUrl
							})
					}
				})
				this.handleEmit()
			},
			// 添加遮罩
			addOverlay(ctx, res, waterMarkingText) {
				let makerHeight = 0
				if (typeDetection.call(this.fontSize) ==
					'[object Array]') {
					for (let i = 0; i < this.fontSize; i++) {
						makerHeight += (this.fontSize[i] + this
							.interval[1])
					}
				} else {
					makerHeight += (this.fontSize * waterMarkingText.length + this
						.interval[1] * (waterMarkingText.length - 1))
				}
				ctx.rect(0, res.height - makerHeight, res.width, makerHeight);
				ctx.fillStyle = "rgba(46, 46, 46,0.4)";
				ctx.fill();
			},
			// 触发emit
			handleEmit() {
				this.$emit('change', this.imageArr)
				this.$emit('input', this.imageArr)
				this.$emit('update:modelValue', this.imageArr)
			},
			sleep(millisecond) {
				return new Promise((resolve) => {
					setTimeout(resolve, millisecond)
				})
			}
		}
	}
</script>
<style lang="scss" scoped>
	.waterMarkingCamera {
		display: flex;
		flex-flow: row;
		align-items: center;
		justify-content: flex-end;
		padding: 20rpx 0;

		&-icon {
			width: 146rpx;
			height: 120rpx;
			border-radius: 4rpx;
			font-size: 42rpx;
			text-align: center;
			line-height: 108rpx;
			border: 2rpx dashed #666666;
		}
	}
</style>
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值