[uni-app] 海报图片分享方案 -canvas绘制

文章目录

      • canvas使用记录
        • 先看下实际效果图
        • 绘制流程及思路
          • 1. 绘制头像, 通过`drawImage`来绘制
          • 2.绘制文字部分
        • 具体代码

分享海报图片的方式,以前再RN端采用的是截图方案, 我记得组件好像是

react-native-view-shot

现在要处理uni-app的海报图片分享, 一般也有 html2canvas的相关插件

不过其缺点也有,
比如说遇到bug,有时候没办法修改什么的
手绘canvas虽然麻烦,但是胜在自由灵活

canvas使用记录

先看下实际效果图

[uni-app] 海报图片分享方案 -canvas绘制_第1张图片

绘制流程及思路

CanvasContext文档

其实使用下来发现, canvas绘制和iOS原生开发进行UI绘制有很多相似之处, 比如draw的入参 ,都需要x,y坐标,设置width/height等

整个canvas绘制的思路如下

0.绘制整个大矩形背景 : 通过uni.downloadFile下载网络图片来获取tempFilePath, 结合几个坐标和宽高参数, 就可以绘制了

1. 绘制头像, 通过drawImage来绘制

不过这里需要注意的是, 如果要对头像图片进行裁剪,比如圆心之类的
需要用到clip的情况下,
需要提前保存好上下文画布

		// 2.顶部头像
				ctx.save(); // 先保存之前的画布
				ctx.beginPath()
				ctx.arc((200 + 190 / 2) * scaleNum, (60 + 190 / 2) * scaleNum, 190 / 2 * scaleNum, 0,
					Math.PI * 2)
				ctx.clip()
				ctx.drawImage(this.headUrl, 200 * scaleNum, 60 * scaleNum, 190 * scaleNum, 190 *
					scaleNum)

(200: 左边间距)
(190: 圆的直径)
(60: top的间距)

(这里需要注意的事, 圆的圆心坐标, 是相对于x/y坐标的哦 )

2.绘制文字部分

这个没有太大的问题, 不过要注意下 textAlign的具体用法
[uni-app] 海报图片分享方案 -canvas绘制_第2张图片

具体代码

  1. html部分
			<canvas :style="{width: boxDetail.width +'px', height: boxDetail.height + 'px' }"
				id="sharePic" canvas-id="sharePic">canvas>

2.js部分

	setCanvasSize() {
				const query = uni.createSelectorQuery().in(this);
				query.select('#content').boundingClientRect(data => {
					this.boxDetail.width = data.width
					this.boxDetail.height = data.height
					console.log(this.boxDetail, data)
					this.$nextTick(() => {
						setTimeout(() => {
							console.log('create and set canvas')
							this.createCanvas()
							// this.asyncAwaitCanvas()
						}, 800)
					})
				}).exec();
			},
			/**
			 * @Description: 创建canvas画布
			 */
			async createCanvas() {
				this.context = uni.createCanvasContext('sharePic', this)
				const ctx = this.context;

				function drawCanvas(url, top) {
					if (url) {
						return new Promise((resolve, reject) => {
							let obj
							obj = { url }
							uni.downloadFile({
								url,
								success: (res) => {
									top ? resolve({ ...res, top }) : resolve({ ...res })
								},
								fail: (err) => {
									reject(err)
								}
							})
						})
					} else {
						return new Promise((resolve, reject) => {
							top ? resolve({ top }) : resolve()
						})
					}
				}
				const scaleNum = this.boxDetail.width / 590
				// 1.背景图
				// - 读背景图资源
				let res = await drawCanvas(
					"https://cdn.froglesson.com/static/cert/share_notes_content_bg.png");
				ctx.save();
				// - 绘制背景图路径/坐标/宽/高
				ctx.drawImage(res.tempFilePath, 0, 0, scaleNum * 590, scaleNum * 864)

				// 2.顶部头像
				ctx.save(); // 先保存之前的画布
				ctx.beginPath()
				ctx.arc((200 + 190 / 2) * scaleNum, (60 + 190 / 2) * scaleNum, 190 / 2 * scaleNum, 0,
					Math.PI * 2)
				ctx.clip()
				ctx.drawImage(this.headUrl, 200 * scaleNum, 60 * scaleNum, 190 * scaleNum, 190 *
					scaleNum)

				// 3.title
				ctx.restore() //恢复一下绘画板
				let title = this.returnName(this.shareData.user_name + ",已学习" + (this.shareData
						.count || 0) +
					"天")
				ctx.setFontSize(32 * scaleNum) //字体大小
				ctx.setFillStyle('#fff') //文字颜色
				ctx.setTextAlign('center') //文本左对其
				ctx.fillText(title, 590 / 2 * scaleNum, (280 + 30) * scaleNum, 530 *
					scaleNum); // marginleft:182rpx;maxWidth:374 最大宽度

				// 4.学习内容
				let content = "今天在「XXXXAPPXXXXX」" + (this.shareData.times > 30 ?
					`${this.shareData.times || 0}分钟` : "学习了")
				content = content + this.studyTitle() + this.studyCount();
				content = "我是中间文字部分,阿爸不不不不不不不不不,啊电话多好多好多好多好等哈,多撒MDJSLSJL"

				// - 字符拆分/计算行数
				let content_top = 0
				let contents = content.trim().split("")
				contents = contents.map((item, index) => {
					if (index && !(index % 20)) {
						item = item + '\n';
					}
					return item
				})
				// 逐行绘制标题文字
				contents.join("").split('\n').forEach((item, index) => {
					ctx.setFontSize(28 * scaleNum) //字体大小
					ctx.setFillStyle('#fff') //文字颜色
					ctx.setTextAlign('center') //文本左对其
					content_top = (365 + 30) * scaleNum + 48 * scaleNum * index //高度计算, 40是行高
					ctx.fillText(item, 590 / 2 * scaleNum, content_top, 530 *
						scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度
				})

				// 5.时间
				let time = this.shareData.day
				ctx.setFontSize(24 * scaleNum) //字体大小
				ctx.setFillStyle('#fff') //文字颜色
				ctx.setTextAlign('center') //文本左对其
				ctx.fillText(time, 590 / 2 * scaleNum, (609 + 30) * scaleNum, 530 *
					scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度

				// 6.icon 
				res = await drawCanvas("https://cdn.froglesson.com/static/cert/index_logo.png")
				ctx.drawImage(res.tempFilePath, 210 * scaleNum, (716 + 30) * scaleNum, 80 * scaleNum,
					80 * scaleNum)

				// 7.qrcode
				res = await drawCanvas(this.qrCode);
				ctx.drawImage(res.tempFilePath, 299 * scaleNum, (716 + 30) * scaleNum, 80 * scaleNum,
					80 * scaleNum)

				// 8.底部title
				let appName = "XXXXAPPNAMEXXXXXX"
				ctx.setFontSize(24 * scaleNum) //字体大小
				ctx.setFillStyle('#fff') //文字颜色
				ctx.setTextAlign('center') //文本左对其
				ctx.fillText(appName, 590 / 2 * scaleNum, (801 + 50) * scaleNum, 530 *
					scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度




				// 完成绘制 - 执行保存
				ctx.draw()
				console.log('绘制完毕')
				uni.setStorageSync('openSaveShareImage', true)
				this.saveOver()
			},

			saveOver() {
				console.log("绘制----------end")
				setTimeout(() => {
					uni.canvasToTempFilePath({
						canvasId: 'sharePic',
						destWidth: this.boxDetail.width * 2, //展示图片尺寸=画布尺寸1*像素比2
						destHeight: this.boxDetail.height * 2,
						quality: 1,
						fileType: 'png',
						success: (result) => {
							console.log(result.tempFilePath)
							uni.saveImageToPhotosAlbum({
								filePath: result.tempFilePath,
								success: function() {
									uni.hideToast()
									uni.showToast({
										icon: 'none',
										title: "图片已下载至【相册】,请打开【相册】查看", // res.tempFilePath
									});
								},
								fail: function() {
									uni.hideToast()

									uni.showToast({
										icon: 'none',
										title: "保存失败", // res.tempFilePath
									});
								}
							})
						},
					}, this);
				}, 1000)
			},

你可能感兴趣的:(uni-app)