小程序 - canvas绘制海报

需求背景

小程序不能直接分享至朋友圈,所以采用海报(最关键的就是邀请码)邀请好友。
从展示层面来讲,就是几张图,通过定位放在一起即可,但是关键是要保存,这个时候,就需要canvas去进行图片合成。那么canvas绘制海报,就拆分两个部分

通过css ,给用户展现的组合图片,这跟简单

通过css 布局,将各个元素组合在一起。这个想必不用多说,
在这里插入图片描述

保存时,通过canvas绘图,将图片合成一个完整的图片

canvas 绘图,有两种模式

  1. 新版 2d 模式(有坑,慎用)
    在这里插入图片描述

  2. 老版 canvas-id=“drawPoster”,虽然操作繁琐,但是兼容性好
    在这里插入图片描述
    获取图片尺寸

downShareImage(imgurl) {
		console.log('imgurl', imgurl);
		return new Promise((resolve, reject) => {
			wx.getImageInfo({
				src: imgurl,
				success: (res) => {
					let { width, height, path } = res;
					resolve({
						width,
						height,
						path,
					});
				},
				fail: () => {
					wx.showToast({
						title: '下载失败',
						icon: 'none',
					});
					reject('下载失败...');
				},
			});
		});
	},

canvas 绘图

	async drawPoster() {
		const { qrCodeImg, avatarImg, downImgRes, nickname } = this.data;
		const ctx = wx.createCanvasContext('drawPoster');
		ctx.drawImage(downImgRes.path, 0, 0, downImgRes.width, downImgRes.height);
		const avatarImgW = 233;
		const qrCodeW = 220;
		const radius = 233 / 2;
		let qrRadious = qrCodeW / 2;
		let avX = downImgRes.width / 2 - avatarImgW + 120;
		let avY = 100;
		let qrX = downImgRes.width / 2 - qrCodeW + 110;
		let qrY = 990;
		ctx.save();
		ctx.beginPath();
		ctx.arc(avX + radius, avY + radius, radius, 0, 2 * Math.PI); // arc方法画曲线,按照中心点坐标计算,所以要加上半径
		ctx.clip();
		ctx.drawImage(avatarImg, avX, avY, 233, 233);
		ctx.restore();
		ctx.setFontSize(50);
		ctx.setTextAlign('center');
		ctx.setFillStyle('#7700d9');
		ctx.fillText(nickname, downImgRes.width / 2, 415);
		ctx.save();
		ctx.arc(qrX + qrRadious, qrY + qrRadious, qrRadious, 0, 2 * Math.PI); // arc方法画曲线,按照中心点坐标计算,所以要加上半径
		ctx.clip();
		ctx.drawImage(qrCodeImg, qrX, qrY, 220, 220);
		ctx.restore();
		ctx.draw(false, async () => {
			let canvasData = await this.exportPoster();
			this.setData({
				canvasData,
			});
		});
	},

canvas合成图片

exportPoster() {
		return new Promise((resolve, reject) => {
			wx.canvasToTempFilePath({
				x: 0,
				y: 0,
				width: 1061,
				height: 1404,
				canvasId: 'drawPoster',
				fileType: 'png',
				success(res) {
					resolve(res.tempFilePath);
				},
				fail() {
					wx.showToast({
						title: '下载失败',
						icon: 'none',
					});
					reject('二维码生成失败');
				},
			});
		});
	},

长按保存:

	saveImage() {
		const { canvasData } = this.data;
		if (!canvasData)
			return wx.showToast({
				title: '海报生成中',
				icon: 'none',
			});
		if (this.drawPosterLock) return;
		this.drawPosterLock = true;
		wx.saveImageToPhotosAlbum({
			filePath: canvasData,
			success: (res) => {
				if (res.errMsg == 'saveImageToPhotosAlbum:ok') {
					wx.showToast({
						title: '保存成功',
					});
				}
			},
			complete: () => {
				this.drawPosterLock = false;
			},
			fail: () => {
				wx.showToast({
					title: '保存失败',
					icon: 'none',
				});
			},
		});
	},

授权逻辑

// 保存为图片
	handlerSavePhoto() {
		let touchTime = this.data.touchEnd - this.data.touchStart;
		console.log('touchTime', touchTime);
		if (touchTime > 800) {
			wx.getSetting({
				success: (res) => {
					var userPhotosAlbum = res.authSetting['scope.writePhotosAlbum'];
					if (userPhotosAlbum) {
						this.saveImage();
						return;
					}
					if (userPhotosAlbum == undefined) {
						//第一次,从未请求过权限
						wx.authorize({
							scope: 'scope.writePhotosAlbum',
							success: (res) => {
								if (res.errMsg == 'authorize:ok') {
									//授权位置弹框中点击 允许
									this.saveImage();
								}
							},
						});
						return;
					}
					if (userPhotosAlbum != 'undefined') {
						// 第一次授权弹框点拒绝的情况 未允许授权
						wx.showModal({
							title: '提示',
							content: '暂无权限,请开启使用权限',
							success: (res) => {
								if (res.confirm) {
									wx.openSetting({
										success: (res) => {
											if (res.authSetting['scope.writePhotosAlbum']) {
												this.saveImage();
											} else {
												// 未授权
											}
										},
									});
								}
							},
						});
					}
				},
			});
		}
	},

总结:

  1. 我们在画canvas总是会担心尺寸问题,并且由尺寸带来的绘图模糊问题。因为小程序单位是rpx(根据不同机型进行适配的单位),但是canvas绘图单位是px,这里有一个好方法。
  • 其实给用户看的,就是一个css布局的图片,用小程序本身的单位,就能解决不同分辨率的问题,而且渲染速度特别快。
  • 用户保存的时候,保存的是canvas画图,合成图片的照片,肯定是px,那么你给canvas宽高,只要和目标图一致即可,或者放大两倍,都是ok的
  1. 由于画图需要拿到数据,并且是异步的,要用await去做同步顺序
  2. 利用promise封装很优美
  3. 用ctx.clip();去裁剪图片,会破坏当前的画布环境 ,所以需要先保存之前的画布环境,然后开始画,画完恢复之前的画布环境
  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
微信小程序通过 Canvas 2D 绘制海报可以用于商品展示、广告宣传等场景。下面介绍一下实现流程: 1. 页面结构 在页面中添加一个 Canvas 标签,设置 id 和宽高: ```html <canvas id="poster" style="width: 750rpx; height: 1334rpx;"></canvas> ``` 2. 获取 Canvas 上下文 在页面的 onLoad 函数中获取 Canvas 上下文: ```javascript let ctx = wx.createCanvasContext('poster'); ``` 3. 绘制背景 使用 Canvas 2D 绘制背景,可以使用 fillRect 方法绘制一个填充矩形: ```javascript ctx.setFillStyle('#ffffff'); ctx.fillRect(0, 0, 750, 1334); ``` 4. 绘制图片 使用 drawImage 方法绘制图片,需要先将图片下载到本地: ```javascript wx.getImageInfo({ src: 'https://example.com/image.png', success: function(res) { ctx.drawImage(res.path, 0, 0, 750, 500); } }); ``` 5. 绘制文本 使用 fillText 或者 strokeText 方法绘制文本,需要设置字体样式和对齐方式: ```javascript ctx.setFontSize(32); ctx.setTextAlign('center'); ctx.fillText('这是一段文本', 375, 600); ``` 6. 保存海报 使用 Canvas 2D 的 toTempFilePath 方法将绘制海报保存到本地: ```javascript ctx.draw(false, function() { wx.canvasToTempFilePath({ x: 0, y: 0, width: 750, height: 1334, canvasId: 'poster', success: function(res) { console.log(res.tempFilePath); } }); }); ``` 完整代码: ```javascript Page({ onLoad: function() { let ctx = wx.createCanvasContext('poster'); ctx.setFillStyle('#ffffff'); ctx.fillRect(0, 0, 750, 1334); wx.getImageInfo({ src: 'https://example.com/image.png', success: function(res) { ctx.drawImage(res.path, 0, 0, 750, 500); } }); ctx.setFontSize(32); ctx.setTextAlign('center'); ctx.fillText('这是一段文本', 375, 600); ctx.draw(false, function() { wx.canvasToTempFilePath({ x: 0, y: 0, width: 750, height: 1334, canvasId: 'poster', success: function(res) { console.log(res.tempFilePath); } }); }); } }); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值