Node 海报功能实践
Node 海报分享实践
最近做了一个 wx 的 node 端海报分享功能,记录一下其中功能的实现,实现一个 express 路由。
使用技术
- node-canvas: 主要用来绘制文字内容。
- canvas-multiline-text: 一个用于绘制多行文字内容的第三方库。
- sharp: 用于处理图片以及进行图片合成的库。
注意: canvas 与 sharp 由于网络限制需要 fq 下载,建议使用阿里镜像进行下载,这里推荐使用 tyarn 进行相关包下载。
背景
常见的 wx 分享,一般而言主要目的是用来推广客户扫描参与活动,所以基本的核心在于三部分:海报背景、文案内容、推广二维码。 主要利用 canvas 进行相关绘制,需要明确相关图片与文字内容的相关坐标。这里是把相关内容与对应坐标,写在一个配置文件里。 坐标配置:
|
|
接口调用的请求入参数:
|
|
合成步骤分大概两步:
- 合成背景图与二维码
- 绘制填写相关文案内容
合成图片
sharp 函数接受一个图片输入,参数为图片路径或者图片的 buffer 数据。
主要利用 sharp 的 composite API 进行图片合成, sharp().composite()
接受一个对象数组,这个对象我们最基本需要用到的三个属性;
- input : 需要合成的图片路径,或者图片的 buffer 数据
- top :合成图片在图片距离顶部的位移
- left :同上,合成图片距离图片左边的位移
|
|
填写文字
利用 canvas 画板,在对应的地方绘制文字。首先我们得读取图片,把图片绘制到画板上,然后利用相关 api 进行文字绘制,具体代码贴下方。
注意:关于 * 2 是为了处理文字内容模糊的问题,具体解释请参照后面。
|
|
这里有两个方法
- bufferToBase64 : 主要是用来将第一步合成的图片 buffer 数据转化成 canvas 可读取的 base64 图片
- drawText: 对绘制文字内容的操作封装
读取图片
|
|
绘制文字
这里的文字内容主要分两种:
- 标题式:主要是单行文本,居中显出。由于设计稿给的标注不太准确,或者容易出现不合理的小数点,这里采用 canvas 的文字居中实现
- 内容式:内容较多,从而产生换行,左对齐。考虑到自动换行需要进行大量的文字计算,这里采用了第三方的库进行处理,具体使用方式请挪步 canvas-multiline-text。
|
|
字体注册
当我们绘制特殊字体的时候,需要先进行注册字体,主要用 canvas 的 registerFont api。
|
|
关于文字居中
关于 node-canvas 文本对齐方式,实现是跟 html canvas 一样的,请参考 MDN Canvas 。需要注意的是,设置 textAlign 后,绘制文字 fillText 的 x 坐标意义就发生了变化,而不是正常的文字最左边所在的坐标。 x 坐标 就是 对齐方式的基准线,比如居中对齐的话,文字居中的中线所在位置就是 x。可以理解为 绘制文字的区域是个矩形,这个矩形中线的位置由 x 控制。
所以当碰到海报是横着的,并且左边有部分区域以没有文字,而文字在右边区域居中的时候,需要设置一个 offest 偏移量进行处理,这也成我的配置中设置了 offset 的缘由,具体如下图所示。
正常的居中,offset 偏移量为 0,则 x 坐标为图片的宽定 width/2。而存在 offset 的时候则是 (width - offset)/2 + offset = (offset + width)/2。所以最终居中的 x 坐标可以统一写成 (offset || 0 + width)/2。
解决字体模糊问题
合成海报后发现字体模糊,主要原因还是 canvas 渲染的问题。高清屏幕一般是 2 个像素合成 1 像素。因此我们需要将 canvas 画板放大后绘制,然后缩小输出,这样就会变的清晰,主要有以下两种方式。
- 直接使用
scale
方法 - 每一个绘制相应的放大,比如我们绘制文字
第一种方法
|
|
这一种方法是最简单省事的,不用转化设置好的坐标参数。但是会有一个问题,字体间距会被放大后并没有缩放回来,最后的字间距会比正常的大两倍。 我最初的想法是通过设置字间距为原本的 1/2,来解决问题,不过,node-canvas 设置字间距的 api 尚未实现,但是已经有开发者提了 pr 实现,尚未被正式支持,故采取了第二种麻烦的方法。 第二种方法**
|
|
这种方法需要手动将所有的大小以及坐标对应的进行转化,繁琐一点,但是避免了字间距过大的问题。 以上参考文章 解决 canvas 在高清屏中绘制模糊的问题