微信小程序之canvas画图

3,003 阅读3分钟

开题

前几天接到个需求,长按图片保存到相册,该图片上有用户头像和昵称以及对应的二维码;那这就不能直接当作图片来操作了,要先把整体图片画出来;我当时用的是canvas。效果图如下:

canvas drawImage

用canvas画图主要使用dramImage API,不太清楚的同学可以区MDN看看:developer.mozilla.org/zh-CN/docs/…

主要是用到图片URL,相对于左上角的x,y坐标以及image在canvas上绘制的宽高。

开始画图

那我们开始行动了,先在html上写个canvas标签:

<canvas
    canvas-id="shareCanvas"
    class="canvas"
    bindlongpress="saveImg"
    catchtouchmove="true"
    style="height: {{canvasHeight + 'rpx'}};width: {{canvasWidth + 'rpx'}};">
</canvas>

在微信小程序上画图先要把图片路径保存到本地,这里我们可以使用wx.getImageInfo或者wx.downloadFile将图片转为本地。考虑到我们要操作多个图片,这里对这些API进行封装:

getImageInfo(src) {
    return new Promise((resolve, reject) => {
        wx.getImageInfo({
            src,
            success: (res) => resolve(res),
            fail: (res) => reject(res)
        })
    });
}

如果你要使用downloadFile也可以,封装方法和上面一样。 封装好了那么我们可以开始画图了,这里需要三张图片,对应的代码如下:

Promise
    .all([
        this.getImageInfo('https://cyt-resource.oss-cn-shanghai.aliyuncs.com/wximg/qrcode_bg.jpg'),
        this.getImageInfo(this.data.userInfo.avatarUrl),
        this.getImageInfo(this.data.qrCodeUrl)
    ])
    .then((res) => {
        const ctx = wx.createCanvasContext('shareCanvas');
        ctx.drawImage(res[0].path, 0, 0, 图片宽度, 图片高度);

        ctx.fillStyle = 'rgb(168, 88, 126)'; // 字体颜色
        ctx.setFontSize(12); // 字体大小
        ctx.fillText(this.data.userInfo.nickname, 相对于左上角的x坐标, 相对于左上角的y坐标);

        ctx.drawImage(res[1].path, x坐标, y坐标, 宽, 高);
        ctx.drawImage(res[2].path, x, y, width, height);

        ctx.draw();

        this.setData({
            isDrawnImg: true
        });
    })
    .catch((e) => {
    });

在这里要说个需要注意的坑,将图片保存到本地需要在小程序后台的downloadFile域名配置里配置好对应的域名,一个都不能落, 比如微信头像需要在downloadFile合法域名配置里加入https://wx.qlogo.cn; 另外需要提醒大家,在微信开发者工具里调试的话尽量不要开不校验合法域名那个选项,即

不然就会出现在调试模式下都是好的,但是真机不开调试就没有效果了。而且不好排查错误。

优化

由于canvas画图需要时间,那我们可以在等待时加个loading效果,在生成或者失败时隐藏loading,失败时还需要提示

wx.showLoading({
    title: '图片生成中...'
});
wx.hideLoading();
wx.showToast({
    title: '图片生成失败'
});

保存图片到相册

这里使用canvasToTempFilePath API,代码如下:

saveImg () {
    wx.canvasToTempFilePath({
        canvasId: 'shareCanvas',
        success: (res) => {
            wx.saveImageToPhotosAlbum({
                filePath: res.tempFilePath,
                success: (res) => {
                    wx.showToast({
                        title: '保存成功'
                    });
                },
                fail: (res) => {
                    this.authorizeToWritePhotosAlbum();
                }
            })
        }
    })
},

这里有个失败时候的操作,主要是因为取消授权后就不能使用了,这里要给个提示,让用户可以再次选择授权进行图片保存,方法如下:

// 授权保存图片至相册
authorizeToWritePhotosAlbum () {
    wx.showModal({
        title: '提示',
        content: '需要您授权保存相册',
        showCancel: false,
        success: (res) => {
          wx.openSetting({
            success: (res) => {
              if (settingdata.authSetting['scope.writePhotosAlbum']) {
                wx.showModal({
                  title: '提示',
                  content: '获取权限成功,再次点击图片即可保存',
                  showCancel: false,
                });
              } else {
                wx.showModal({
                  title: '提示',
                  content: '获取权限失败,将无法保存到相册哦~',
                  showCancel: false,
                });
              }
            }
          })
        }
    });
},

至此,画图以及保存到相册的问题都有解决了。那么如果我们需要适配呢,图片需要自适应。在微信小程序我们可以使用rpx进行自适应:可以看看文档 developers.weixin.qq.com/miniprogram…

自适应

因为drawImage里面的单位都是px,且不能更改,那我们应该怎么进行自适应。可以根据设置的 设备屏幕/750*自己设置的自适应宽度(单位为rpx),比如设置宽度为600rpx,那么在drawImage里就是设备屏幕/750*600;要获取屏幕高度直接调用wx.getSystemInfo就行了,取返回结果的screenWidth;文字大小,小图片的x,y坐标都可以通过这个方法进行自适应。

结题

我当时主要是域名配置忘了,坑了自己,然后就是授权问题(关闭后无法保存,自己懵了,没考虑到);希望大家都能注意下,写的不好的地方希望大家提出来,一大早写的比较匆忙,😄