小程序 <https://link.zhihu.com/?target=http%3A//www.wxapp-union.com/>海报组件

https://github.com/jasondu/wx...
<https://link.zhihu.com/?target=https%3A//github.com/jasondu/wxa-plugin-canvas>

需求


小程序分享到朋友圈只能使用小程序码海报来实现,生成小程序码的方式有两种,一种是使用后端方式,一种是使用小程序自带的canvas生成;后端的方式开发难度大,由于生成图片耗用内存比较大对服务端也是不小的压力;所以使用小程序的canvas是一个不错的选择,但由于canvas水比较深,坑比较多,还有不同海报需要重现写渲染流程,导致代码冗余难以维护,加上不同设备版本的情况不一样,因此小程序海报生成组件的需求十分迫切。

在实际开发中,我发现海报中的元素无非一下几种,只要实现这几种,就可以通过一份配置文件生成各种各样的海报了。

海报中的元素分类



要解决的问题

* 单位问题
* canvas隐藏问题
* 圆角矩形、圆角图片
* 多段文字
* 超长文字和多行文字缩略问题
* 矩形包含文字
* 多个元素间的层级问题
* 图片尺寸和渲染尺寸不一致问题
* canvas转图片
* IOS 6.6.7 clip问题
* 关于获取canvas实例
单位问题

canvas绘制使用的是px单位,但不同设备的px是需要换算的,所以在组件中统一使用rpx单位,这里就涉及到单位怎么换算问题。

通过wx.getSystemInfoSync获取设备屏幕尺寸,从而得到比例,进而做转换,代码如下:
const sysInfo = wx.getSystemInfoSync(); const screenWidth =
sysInfo.screenWidth; this.factor = screenWidth / 750; // 获取比例 function
toPx(rpx) { // rpx转px return rpx * this.factor; } function toRpx(px) { //
px转rpx return px / this.factor; },
canvas隐藏问题

在绘制海报过程时,我们不想让用户看到canvas,所以我们必须把canvas隐藏起来,一开始想到的是使用display:none;
但这样在转化成图片时会空白,所以这个是行不通的,所以只能控制canvas的绝对定位,将其移出可视界面,代码如下:
.canvas.pro { position: absolute; bottom: 0; left: -9999rpx; }
圆角矩形、圆角图片

由于canvas没有提供现成的圆角api,所以我们只能手工画啦,实际上圆角矩形就是由4条线(黄色)和4个圆弧(红色)组成的,如下:



圆弧可以使用canvasContext.arcTo这个api实现,这个api的入参由两个控制点一个半径组成,对应上图的示例
canvasContext.arcTo(x1, y1, x2, y2, r)
接下来我们就可以非常轻松的写出生成圆角矩形的函数啦
/** * 画圆角矩形 */ _drawRadiusRect(x, y, w, h, r) { const br = r / 2;
this.ctx.beginPath(); this.ctx.moveTo(this.toPx(x + br), this.toPx(y)); //
移动到左上角的点 this.ctx.lineTo(this.toPx(x + w - br), this.toPx(y)); // 画上边的线
this.ctx.arcTo(this.toPx(x + w), this.toPx(y), this.toPx(x + w), this.toPx(y +
br), this.toPx(br)); // 画右上角的弧 this.ctx.lineTo(this.toPx(x + w), this.toPx(y +
h - br)); // 画右边的线 this.ctx.arcTo(this.toPx(x + w), this.toPx(y + h),
this.toPx(x + w - br), this.toPx(y + h), this.toPx(br)); // 画右下角的弧
this.ctx.lineTo(this.toPx(x + br), this.toPx(y + h)); // 画下边的线
this.ctx.arcTo(this.toPx(x), this.toPx(y + h), this.toPx(x), this.toPx(y + h -
br), this.toPx(br)); // 画左下角的弧 this.ctx.lineTo(this.toPx(x), this.toPx(y +
br)); // 画左边的线 this.ctx.arcTo(this.toPx(x), this.toPx(y), this.toPx(x + br),
this.toPx(y), this.toPx(br)); // 画左上角的弧 }
如果是 画线框 就使用 this.ctx.stroke();

如果是 画色块 就使用 this.ctx.fill();

如果是 圆角图片 就使用
this.ctx.clip(); this.ctx.drawImage(***);
clip() 方法从原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。可以在使用
clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。

多段文字

如果是连续多段不同格式的文字,如果让用户每段文字都指定坐标是不现实的,因为上一段文字的长度是不固定的,这里的解决方案是使用 ctx.measureText
 (基础库 1.9.90 开始支持)Api来计算一段文字的宽度,记住这里返回宽度的单位是px( 坑 ),从而知道下一段文字的坐标。

超长文字和多行文字缩略问题

设置文字的宽度,通过 ctx.measureText
 知道文字的宽度,如果超出设定的宽度,超出部分使用“...”代替;对于多行文字,经测试发现字体的高度大约等于字体大小,并提供lineHeight参数让用户可以自定义行高,这样我们就可以知道下一行的y轴坐标了。

矩形包含文字

这个同样使用 ctx.measureText 接口,从而控制矩形的宽度,当然这里用户还可以设置paddingLeft和paddingRight字段;

文字的垂直居中问题可以设置文字的基线对齐方式为middle( this.ctx.setTextBaseline('middle');
 ),设置文字的坐标为矩形的中线就可以了;水平居中 this.ctx.setTextAlign('center'); ;



多个元素间的层级问题


由于canvas没有Api可以设置绘制元素的层级,只能是根据后绘制层级高于前面绘制的方式,所以需要用户传入zIndex字段,利用数组排序(Array.prototype.sort)后再根据顺序绘制。

图片尺寸和渲染尺寸不一致问题

绘制图片我们使用 ctx.drawImage() API;

如果使用 drawImage(dx, dy, dWidth, dHeight) ,图片会压缩尺寸以适应绘制的尺寸,图片会变形,如下图:

在基础库1.9.0起支持 drawImage(sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
,sx和sy是源图像的矩形选择框左上角的坐标,sWidth和sHeight是源图像的矩形选择框的宽度和高度,如下图:



如果绘制尺寸比源图尺寸宽,那么绘制尺寸的宽度就等于源图宽度;反之,绘制尺寸比源图尺寸高,那么绘制尺寸的高度等于源图高度;

我们可以通过 wx.getImageInfo Api获取源图的尺寸;

canvas转图片

在canvas绘制完成后调用 wx.canvasToTempFilePath Api将canvas转为图片输出,这样需要注意, 
wx.canvasToTempFilePath 需要写在 this.ctx.draw 的回调中,并且在组件中使用这个接口需要在第二个入参传入this( 坑
 ),如下
this.ctx.draw(false, () => { wx.canvasToTempFilePath({ canvasId: 'canvasid',
success: (res) => { wx.hideLoading(); this.triggerEvent('success',
res.tempFilePath); }, fail: (err) => { wx.hideLoading();
this.triggerEvent('fail', err); } }, this); });
IOS 6.6.7 clip问题

在IOS 6.6.7版本中clip方法连续裁剪图片时,只有第一张有效,这是微信的bug,官方也证实了( 
https://developers.weixin.qq....
<https://link.zhihu.com/?target=https%3A//developers.weixin.qq.com/community/develop/buglist>
 )

关于获取canvas实例

我们可以使用 wx.createCanvasContext 获取小程序实例,但在组件中使用切记第二个参数需要带上this,如下
this.ctx = wx.createCanvasContext('canvasid', this);
如何使用组件

https://github.com/jasondu/wx...
<https://link.zhihu.com/?target=https%3A//github.com/jasondu/wxa-plugin-canvas>

专栏作家

weizaidu。小程序社区博主,坚持原创分享技术博文。

本文原创发布于小程序社区。未经许可,禁止转载

原文地址:轻松生成小程序分享海报-小程序社区/博主专区-微信小程序开发社区-微信小程序联盟
<https://link.zhihu.com/?target=http%3A//www.wxapp-union.com/forum.php%3Fmod%3Dviewthread%26tid%3D39773%26extra%3Dpage%253D1>

相关文章:

小程序生成海报保存分享图片完全指南
<https://link.zhihu.com/?target=http%3A//www.wxapp-union.com/portal.php%3Fmod%3Dview%26aid%3D4399>

微信小程序朋友圈分享图片生成方案实现
<https://link.zhihu.com/?target=http%3A//www.wxapp-union.com/portal.php%3Fmod%3Dview%26aid%3D4357>

微信小程序中的分享事件
<https://link.zhihu.com/?target=http%3A//www.wxapp-union.com/portal.php%3Fmod%3Dview%26aid%3D3535>

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信