如果你的小程序需要分享到朋友圈,当前通用做法是利用小程序 Canvas 绘制一张带有小程序二维码的海报图,然后引导用户将海报图保存到本地相册后再分享到朋友圈。
然鹅,小程序的 Canvas 生成方法实现起来较难用且坑多,关于小程序 Canvas 的坑,可以查看此仓库。因此,我们这里用一个三方库 Painter 来实现绘制海报的需求。
Painter
Painter 能轻松通过简单的 Json 配置,绘制海报图,它有以下几点优势:
- 功能全,支持文本、图片、矩形、qrcode 类型的 view 绘制
- 布局全,支持多种布局方式,如 align(对齐方式)、rotate(旋转)
- 支持圆角,其中图片,矩形,和整个画布支持 borderRadius 来设置圆角
- 性能优化,Painter 对网络素材图片加载实现了一套 LRU 存储机制,不用重复下载素材图片。
- 容错,因为某些特殊情况会导致 Canvas 绘图不完整。Painter 对此加入了对结果图片进行检测机制,如果绘图出错会进行重绘。
Painter 库的整体架构如下:
绘制海报
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
字段作为画图数据的数据源imgOK
或imgErr
事件来获得成功后的图片,或失败的原因widthPixels
用于设置来强制指定生成的图片的像素宽度,否则,会根据你画布中设置的大小来动态调节,比如你用了rpx
,则在 iphone 6 上会生成 0.5 倍像素的图片。由于 Canvas 绘制的图片像素直接由 Canvas 本身大小决定,此处通过同比例放大整个画布来实现对最后生成的图片大小的调节。
当我们调用生成海报事件时,只需将 Json 数据源 data
赋予 Painter 属性 palette
即可绘制海报图,是的,就是这么简单。点击此处 查看 Painter 更多属性设置。
下方有完整示例代码。
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;}