04月29, 2019

小程序canvas生成分享封面和朋友圈海报

生成分享封面和朋友圈海报在小程序中是一个比较常见的需求。小程序默认分享是截图当前页面再配上标题简介分享给好友或者微信群,默认生成的截图的效果并不太好,而且也无法分享到朋友圈,所以生成生成一个自定义的封面分享给朋友会有一个比较好的体验,也更容易获取流量,此外生成一个海报发布到朋友圈也是一个宣传小程序,获取流量的途径。

生成海报的方法

生成海报有两种方法:服务端生成、小程序端生成。

服务端自然是可以生成图片的,不过会占用一些服务器性能,另外某些时候生成图片可能需要绘制文字,而绘制文字就需要在服务器上安装字体。

小程序端也可以使用canvas生成图片,比较灵活,调试起来也比较直观。接下来就讨论一下如何生成这样一个海报图。这里只给出实现方法,没有完整代码。

海报.png

添加画布

小程序绘图需要用到canvas组件,首先我们在wxml里面添加一个canvas组件。

<canavs canvas-id='poster' style='width:670px;height:1334px'>

注意:

  • 小程序中canvas没有widthheight属性,依靠style来控制。
  • 此外,应该将canvas的尺寸设置为实际显示的两倍或者三倍,以适应高倍屏,否则绘制出来的图片在高倍屏显示的时候会显示非常模糊。
  • 绘制海报的canvas应该离屏,不能影响其他UI的展示,可以使用绝对定位将画布移除屏幕,或者使用其他方法。

获取canvas绘图上下文

const ctx = wx.createCanvasContext('poster', this);

图片加载与绘制

绘制到canvas的图片需要先下载。

const wxGetImageInfo = promisify(wx.getImageInfo)

const image = await wxGetImageInfo({
    src: 'http://some-domain/bg.png'
});
ctx.drawImage(image,path, 0, 0, 670, 1334);
ctx.draw();//必须调用draw方法,才能将路径或者图片绘制到画布上

绘制文字

以上面的海报为例,可以看到海报上面还有一些文字。可以调用ctx.fillText来绘制,不多赘述。

文字长度问题

可以看到上面海报中有一个带间隔且有粗体的文字,所以这里会出现一个文字长度的问题,用于定位后面文字的绘制位置。

const textWidth = ( ctx.measureText() ).width;

measureText接收一个字符串,并给给其在画布上绘制时所占据的空间,目前只有宽度信息,没有高度。

文字加粗

基础库1.9.90 之前,智能使用ctx.setFontSize 来设置字体大小。不过使用过程中发现,使用ctx.setFontSize('normal bold 14px sans-serif')也是可以实现文字的字体、加粗、大小等设置的。 基础库1.9.90 之后,可以使用ctx.font,例如:ctx.font = 'normal bold 14px sans-serif'。 当然也有看到有些玩家用一些小窍门来实现字体加粗的,比如讲字体增大2px,然后绘制的时候将绘制位置的Y轴向上移动1px来实现。

圆角问题

仔细查看上面的海报,可以发现海报其实是有圆角的(博客自动给图片加了边框,所以看起来很奇怪)。然后画布中不像css中可以直接给定圆角,圆角的实现可以通过绘制路径,然后裁剪来实现。

具体思路是,使用ctx.arc(x, y, r, startAngle, endAngle)接口,配合lineTo来实现。即,在矩形的每个角落使用arc来画一个90度的扇形,然后将各边连接起来,最后使用clip裁剪,从而实现一个带圆角的矩形。

说的比较简单,没有明白,可以具体看一下代码:

function drawRadius(ctx, x, y, w, h, r) {
    ctx.beginPath();
    ctx.save();
    ctx.setFillStyle('transparent');

    // border-top
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);

    // 右上角
    ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, 0, false);

    // border-right
    ctx.lineTo(x + w, y + h - r);

    // 右下角
    ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5, false);

    // border-bottom
    ctx.lineTo(x + r, y + h);
    // 左下角
    ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI, false);

    // border-left
    ctx.lineTo(x, y + r);
    // 左上角
    ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5, false);

    ctx.fill();
    ctx.closePath();
    ctx.restore();
    // 剪切
    ctx.clip();
}

canvas转图片或存储

canvas绘制好不可以直接用于保存,或者展示为图片,需要先做一个转换。类似于H5中canvas.toDataURL(),小程序也提供了wx.canvasToTempFilePath()接口,这是一个异步的过程。

ctx.draw(false, () => {
    wx.canvasToTempFilePath({
        canvasId,
        success(res){
            resolve(res.tempFilePath);
        }
    });
});

需要注意,ctx.draw也是一个异步的接口,同时,wx.canvasToTempFilePath需要在ctx.draw的回调中调用,确保转换图片时图片已经绘制完成了。

异步过程与分享

这里有一个问题一直没有提到。不像朋友圈海报的生成,该过程只需要点击按钮后,进行图片绘制,然后转换为临时文件,最后保存到相册中即可(保存相册需要授权)。

自定义分享给朋友或微信群依靠onShareAppMessage事件来实现。这是一个同步的接口,接口最后给出对应的文案与图片配置即可。

回顾图片的生成过程,可以看到,其中有多个异步过程:下载图片(wx.getImageInfo)、绘制画布(ctx.draw)、canvas转临时文件(wx.canvasToTempFilePath)。这些异步过程会导致无法正确的给出分享配置。

要想在分享的时候,给出正确的图片,就需要提前绘制分享封面,尽可能早的加载图片,onLoad的时候,就开始绘制分享封面,如果用户点击分享按钮的时候,还未生成好分享封面可以给出一个默认的分享图或者使用小程序自行生成的截图做兜底。

本文链接:https://luodao.me/post/mp-canvas-poster.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。