草庐IT

用canvas绘制微信小程序海报页面并保存相册-适用微信原生

菜鸡爱上编程 2023-04-03 原文

微信小程序绘制海报并保存相册

tip:代码中使用的是uni的api 如果使用原生微信小程序开发,可以把uni更换成wx使用

文章目录


前言

本片文章主要是把我工作中实际用到,可以整合出来的功能,做一个总结和讲解。

1、如何使用canvas
2、canvas绘制后如何生成图片
3、将图片保存到相册
4、难点:如何解决canvas层级高于position定位层级问题,如何适配不同屏幕大小比例


一、分析需求

两种方案:

  1. 进入页面先绘制canvas,点击按钮后再生成图片并保存相册
  2. 进入页面先绘制canvas,并生成图片,点击按钮直接保存图片到相册

我这边使用的是第一种方式,看实际需求。(其实没多大区别)

二、准备数据

在开始绘制canvas之前首先要,分析页面中用到了哪些内容:

  1. 背景图片
  2. 二维码图片
  3. 关于海报的一些描述性文案

注意:
1、由于这些数据可能不是页面写死的内容,是根据api返回的数据,所以需要在绘制canvas之前就先准备好从api返回的数据。
2、需要考虑同步问题


三、编码开始

先看下页面图片样式

html部分

<canvas :style="{height:pageHeight,width: _width +'px'}" canvas-id="mycanvas" />
<cover-view @tap="saveImg">
	<cover-image src="btn_img.png"></cover-image>
</cover-view>

解析:

1、首先需要准备一个canvas用于绘制页面内容的载体,id是唯一标识
2、需要一个按钮用于保存图片,但是发现即使用绝对定位调整层级还是不能将按钮显示到canvas上层,所以借助于官方提供的覆盖原生组件的方法。cover-viewcover-image

js部分

这里只说代码重点部分。

页面中用到的变量可以自行修改,至于变量的初始值都可以是空字符串

1、准备好数据后开始绘制

onReady() {
	// 为了兼容不同机型页面大小,所以需要先获取到页面宽高
	this.getSysInfo()
},
getSysInfo() {
	/*获取手机宽高*/
	let that = this
	let imgUrl = this.imageUrl
	let qrcodeUrl = this.codeUrl
	uni.getSystemInfo({
		success(res) {
			console.log('屏幕宽度', res)
			that._width = res.windowWidth
			that._heigth = res.windowHeight
			// 获取图片信息生成canvas
			// 因为这里用的网络图片所以需要先获取本地网络图地址
			that.getImginfo([imgUrl, qrcodeUrl], 0);
		}
	})
},
getImginfo(urlArr, _type) {
	let that = this;
	uni.getImageInfo({
		src: urlArr[_type],
		success: function(res) {
			//res.path是网络图片的本地地址
			if (_type === 0) { //
				that.localImageUrl = res.path
				that.getImginfo(urlArr, 1)
			} else {
				//二维码
				that.localCodeUrl = res.path
				// 创建canvas图片
				that.createNewImg();
			}
		},
		fail: function(res) {
			//失败回调
			console.log('错误', _type, res)
		}
	});
},
createNewImg() {
	let that = this;
	let ctx = uni.createCanvasContext('mycanvas');
	// 绘制背景
	ctx.drawImage(this.localImageUrl, 0, 0, this._width, this._heigth - 44 - this.statusBarHeight);
	// 绘制二维码区域
	ctx.lineJoin = "round";
	ctx.lineWidth = 12;
	ctx.setFillStyle('#FFFFFF')
	ctx.setStrokeStyle('#FFFFFF')

	// 二维码区域y坐标
	const yPosition = this._heigth - 44 - this.statusBarHeight - this.remSize(98) - 100
	ctx.strokeRect(this.remSize(24), yPosition, this.remSize(326), this.remSize(98));
	ctx.fillRect(this.remSize(24), yPosition, this.remSize(326), this.remSize(98));

	// 绘制二维码
	const yPosition2 = yPosition + 4
	ctx.drawImage(this.localCodeUrl, this.remSize(28), yPosition2, this.remSize(90), this.remSize(90));

	//二维码区域文字
	let title1 = this.tips.title1;
	let title2 = this.tips.title2;
	let title3 = this.tips.title3;
	// 文字一
	ctx.setFontSize(this.remSize(14));
	ctx.setFillStyle('#333333');
	ctx.fillText(title1, this.remSize(128), yPosition + this.remSize(30));
	// 文字二三
	ctx.setFillStyle('#B8B8B8');
	ctx.setFontSize(this.remSize(12));
	ctx.fillText(title2, this.remSize(128), yPosition + this.remSize(62));
	ctx.fillText(title3, this.remSize(128), yPosition + this.remSize(85));
	// 绘制
	ctx.draw();

	//将生成好的图片保存到本地
	uni.canvasToTempFilePath({
		canvasId: 'mycanvas',
		success: (res)=> {
			that.loadImagePath = res.tempFilePath
		},
		fail: (res)=> {
			console.log(res);
		}
	});
},
// 缩放比例
let scalePage = uni.getSystemInfoSync().windowWidth / 375

remSize(num) {
	return num * scalePage
},

解释:

1、uni.createCanvasContext创建canvas上下文,用于操作绘制具体图片和文字
2、ctx.drawImage用于绘制图片
3、ctx.setFillStyle用于绘制填充色
4、ctx.setStrokeStyle用于绘制边框颜色
5、因为canvas不能绘制圆角实心矩形,所以取巧的方式,就是用圆角矩形和实心矩形覆盖的方式,效果见上图。
6、ctx.strokeRect绘制矩形空心
7、ctx.fillRect绘制矩形实心
6、ctx.setFontSize ctx.setFillStyle ctx.fillText 绘制文字大小,颜色,位置
7、ctx.draw() 把所有的上下文内容,绘制到canvas上面
特别说明:

  1. remSize方法,根据设计稿去设置图片大小和文案的位置比例,我这边是根据375像素的宽度计算比例。
  2. 这些api具体使用方法,可以参考官方文档,这里就不一一列举了。

2、保存图片

//点击保存到相册
saveImg() {
	uni.saveImageToPhotosAlbum({
		filePath: this.loadImagePath,
		success(res) {
			uni.showToast({
				title: '已保存到相册',
				icon: 'success',
				duration: 3000
			})
		}
	})
},

解析:

1、因为保存按钮是固定定位,并且按钮并非canvas绘制内容,所以保存图片的时候并不会有按钮。
2、需要注意的是,保存相册是需要相册保存权限的,这个可以看文档自己去设置。

总结

上面的说的覆盖问题,还可以是先使用canvas把直接生成图片,然后在页面上直接显示生成好的图片。
如有问题,欢迎指正修改。

有关用canvas绘制微信小程序海报页面并保存相册-适用微信原生的更多相关文章

  1. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  2. 微信小程序通过字典表匹配对应数据 - 2

    前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立

  3. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  4. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  5. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

    我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

  6. ruby - 在 ASP 页面上 Mechanize 中断 - 2

    require'mechanize'agent=Mechanize.newlogin=agent.get('http://www.schoolnet.ch/DE/HomeDE.htm')agent.clicklogin.link_withtext:/Login/然后我得到Mechanize::UnsupportedSchemeError。 最佳答案 Mechanize不支持javascript但您可以将搜索字段添加到表单并为其分配搜索词并使用mechanize提交表单form=page.forms.firstform.add_fie

  7. ruby-on-rails - Rails 3.2 防止使用错误保存对象 - 2

    我有一个ActiveRecord对象,我想在不对模型进行永久验证的情况下阻止它被保存。您过去可以使用errors.add执行类似的操作,但它看起来不再有效了。user=User.lastuser.errors.add:name,"namedoesn'trhymewithorange"user.valid?#=>trueuser.save#=>true或user=User.lastuser.errors.add:base,"myuniqueerror"user.valid?#=>trueuser.save#=>true如何在不修改用户对象模型的情况下防止将用户对象保存在Rails3.2中

  8. ruby-on-rails - prawnto 显示新页面时不会中断的表格 - 2

    我有可变数量的表格和可变数量的行,我想让它们一个接一个地显示,但如果表格不适合当前页面,请将其放在下一页,然后继续。我已将表格放入事务中,以便我可以回滚然后打印它(如果高度适合当前页面),但我如何获得表格高度?我现在有这段代码pdf.transactiondopdf.table@data,:font_size=>12,:border_style=>:grid,:horizontal_padding=>10,:vertical_padding=>3,:border_width=>2,:position=>:left,:row_colors=>["FFFFFF","DDDDDD"]pdf.

  9. ruby-on-rails - 将保存回调添加到单个 ActiveRecord 实例,可以吗? - 2

    是否可以为单个ActiveRecord实例添加回调?作为进一步的限制,这是继续使用库,所以我无法控制该类(除了对其进行猴子修补)。这或多或少是我想做的:defdo_something_creazymessage=Message.newmessage.on_save_call:do_even_more_crazy_stuffenddefdo_even_more_crazy_stuff(message)puts"Message#{message}hasbeensaved!Hallelujah!"end 最佳答案 你可以通过在创建对象后立

  10. ruby - 正则表达式 - 保存重复捕获的组 - 2

    这就是我做的a="%span.rockets#diamonds.ribbons.forever"a=a.match(/(^\%\w+)([\.|\#]\w+)+/)putsa.inspect这是我得到的#这就是我想要的#帮助?我尝试过但失败了:( 最佳答案 通常,您不能获得任意数量的捕获组,但如果您使用扫描,您可以为您想要捕获的每个标记获得一个匹配:a="%span.rockets#diamonds.ribbons.forever"a=a.scan(/^%\w+|\G[.|#]\w+/)putsa.inspect["%span","

随机推荐