使用 Painter 通过 Json 配置轻松生成小程序海报图

程序员

如果你的小程序需要分享到朋友圈,当前通用做法是利用小程序 Canvas 绘制一张带有小程序二维码的海报图,然后引导用户将海报图保存到本地相册后再分享到朋友圈。

然鹅,小程序的 Canvas 生成方法实现起来较难用且坑多,关于小程序 Canvas 的坑,可以查看此仓库。因此,我们这里用一个三方库 Painter 来实现绘制海报的需求。

Painter

Painter 能轻松通过简单的 Json 配置,绘制海报图,它有以下几点优势:

  • 功能全,支持文本、图片、矩形、qrcode 类型的 view 绘制
  • 布局全,支持多种布局方式,如 align(对齐方式)、rotate(旋转)
  • 支持圆角,其中图片,矩形,和整个画布支持 borderRadius 来设置圆角
  • 性能优化,Painter 对网络素材图片加载实现了一套 LRU 存储机制,不用重复下载素材图片。
  • 容错,因为某些特殊情况会导致 Canvas 绘图不完整。Painter 对此加入了对结果图片进行检测机制,如果绘图出错会进行重绘。

Painter 库的整体架构如下:
painter-minip-1

绘制海报

1.下载 Painter

通过 git 下载 Painter 源代码,并将 components 目录拷贝到自己的项目中。

2.引入 Painter

app.json 文件中作为自定义组件引入。

{  "usingComponents": {    "painter": "/components/painter/painter"  }}

3.使用 Painter

我们在 index.wxml 中直接使用 painter 组件:

<painter customStyle='position: absolute; left: -9999rpx;' palette="{{data}}" bind:imgOK="onImgOK" bind:imgErr="onImgErr" widthPixels="1000"/>
  • palette 字段作为画图数据的数据源
  • imgOKimgErr 事件来获得成功后的图片,或失败的原因
  • widthPixels 用于设置来强制指定生成的图片的像素宽度,否则,会根据你画布中设置的大小来动态调节,比如你用了 rpx,则在 iphone 6 上会生成 0.5 倍像素的图片。由于 Canvas 绘制的图片像素直接由 Canvas 本身大小决定,此处通过同比例放大整个画布来实现对最后生成的图片大小的调节。

当我们调用生成海报事件时,只需将 Json 数据源 data 赋予 Painter 属性 palette 即可绘制海报图,是的,就是这么简单。点击此处 查看 Painter 更多属性设置。

下方有完整示例代码。

4.海报效果图

painter-minip-4

完整代码

全局 app.json 或 页面.json

{  "usingComponents": {    "painter": "/components/painter/painter"  }}

index.wxml

<button type="default" bindtap="handleCreateCanvas">生成海报</button><!-- 引入 painter组件  --><painter customStyle='position: absolute; left: -9999rpx;' palette="{{data}}" bind:imgOK="onImgOK" bind:imgErr="onImgErr" widthPixels="1000"/><!-- 预览海报  --><view hidden='{{canvasPreview}}' class='preview'>  <view class="content">    <view style="margin-top: 20rpx;">      <image src='{{canvasUrl}}' mode='widthFix'></image>    </view>    <view style="margin-top: 30rpx;">      <button round type="primary" bindtap="handleSave">保存到相册并分享</button>    </view>    <view style="margin-top: 20rpx;">      <icon size="40" color="#ffffff" name="close" bindtap="onCloseCanvas" />    </view>  </view></view>

index.js

/**   * 生成海报   */  handleCreateCanvas() {    const that = this;    wx.showLoading({      title: '生成海报中...',      mask: true    })    // 设置海报数据    this.setData({      data: {        width: `600rpx`,        height: `1080rpx`,        background: '#fff',        borderRadius: '4px',        views: [{            type: 'image',            url: '表示图片资源的地址,本地或网络',            css: {              top: '10px',              right: '20px',              left: '20px',              borderRadius: '20px',              width: '600rpx',              height: '800rpx'            }          },          {            type: 'text',            text: '没有武器,仇恨,和暴力',            css: {              left: '20px',              top: '820rpx',              fontSize: '18px',              color: "#262626",              width: '600rpx',              maxLines: 2            },          },          {            type: 'text',            text: '6.7',            css: {              right: '30px',              top: '580rpx',              fontSize: '28px',              fontWeight: 'bold',              color: "#ffffff"            },          },          {            type: 'image',            url: '小程序二维码的地址,本地或网络',            css: {              bottom: '35rpx',              left: "260rpx",              width: '120rpx',              height: '120rpx',            },          },          {            type: 'text',            text: '分享自 · XX电影',            css: {              bottom: '10rpx',              fontSize: '10px',              color: '#bfbfbf',              left: '325rpx',              align: 'center',            },          }        ],      }    })  },  /**  * 生成海报成功  */  onImgOK(e) {    this.setData({      canvasUrl: e.detail.path,      canvasPreview: false    })    wx.hideLoading()  },  /**  * 生成海报失败  */  onImgErr(e) {    wx.showToast({      title: '生成海报失败',      icon: 'none',      duration: 2000    })  },    /**  * 关闭预览  */  onCloseCanvas() {    this.setData({      canvasPreview: true    })  },    /**  * 保存海报到相册  */  handleSave() {    wx.saveImageToPhotosAlbum({       filePath: that.data.canvasUrl,       success(res) {},       fail(res) {},    })        }

index.wxss

.preview {  width: 100%;  min-height: 100vh;  background: rgba(0, 0, 0, 0.7);  position: absolute;  z-index: 2;  top: 0;}.preview .content {  z-index: 99;  display: flex;  flex-direction: column;  justify-content: center;  align-items: center;}