前言:
开发过微信小程序的童鞋应该使用过小程序的海报分享图插件,由于微信小程序以前无法分享到朋友圈,开发者萌生的想法是:将图片+小程序码+文字使用canvas进行合成生成海报,再导出到本地,让用户手动发朋友圈进行分享,而支付宝这边因为社交能力属性较弱,业务中需要生成海报的功能较少,而我们业务里刚好有该需求,所以在实现该功能后在这里进行一次分享。
在进行支付宝海报图生成之前,查阅了支付宝相关的文档,搜索了类似的组件,发现只有IOT上有个海报组件,后来官方大佬推荐了一个组件库:MARS组件库(该组件库里有生成海报的组件,并且也是使用的我们下面将要分享的插件进行的二次封装),但是由于不开源,并且发现了一些小bug,导致暂时未使用该组件库。当然大多数功能还是很完善的,有需要支付宝UI组件库的同学可以了解下。
既然无法实现,那么只能改造已有且比较出名的了,这里选用了Painter,因Painter官方也没有支付宝版本,所以这里做一次迁移转换,期间遇到挺多差异和坑的。
目前已完全转换为支付宝小程序版本:
使用方法如下:
- 引用组件:
{ "usingComponents": { "painter": "/components/painter/index" } }
- 使用组件:
<painter a:if="{{startInit}}" palette="{{imgDraw}}" onSuccess="onSuccess" onError="onError"></painter> <image src="{{imagePath}}" mode="widthFix" style="width: 100%;" />
- 开始绘图:
handleCreatePoster() { my.showLoading({ content: '生成中...' }) this.setData({ startInit: true // 开始动态加载painter组件。 }, () => { this.setData({ imgDraw: { width: '750rpx', height: '1334rpx', background: 'https://qiniu-image.qtshe.com/20190506share-bg.png', views: [ { type: 'image', url: 'https://qiniu-image.qtshe.com/1560248372315_467.jpg', css: { top: '32rpx', left: '30rpx', right: '32rpx', width: '688rpx', height: '420rpx', borderRadius: '16rpx' }, }, { type: 'image', url: 'https://qiniu-image.qtshe.com/default-avatar20170707.png', css: { top: '404rpx', left: '328rpx', width: '96rpx', height: '96rpx', borderWidth: '6rpx', borderColor: '#FFF', borderRadius: '96rpx' } }, { type: 'text', text: '青团社-郝帅', css: { top: '532rpx', fontSize: '28rpx', left: '375rpx', align: 'center', color: '#3c3c3c' } }, { type: 'text', text: `邀请您参与助力活动`, css: { top: '576rpx', left: '375rpx', align: 'center', fontSize: '28rpx', color: '#3c3c3c' } }, { type: 'text', text: `宇宙最萌蓝牙耳机测评员`, css: { top: '644rpx', left: '375rpx', maxLines: 1, align: 'center', fontWeight: 'bold', fontSize: '44rpx', color: '#3c3c3c' } }, { type: 'image', url: 'https://mass.alipay.com/wsdk/img?fileid=A*qwhmTo6OiEAAAAAAAAAAAAAAAQAAAQ&bz=am_afts_openhome&zoom=original.jpg', css: { top: '834rpx', left: '470rpx', width: '200rpx', height: '250rpx' } } ] } }) }) }, // 绘制成功 onSuccess(res) { my.hideLoading() this.setData({ imagePath: res.path }) }, // 绘制失败(在增加了失败重试5次的情况下还是绘制失败) onError(err) { my.hideLoading() my.showToast({ content: '生成失败', icon: 'error' }) }, // 保存海报图 downloadImg() { my.saveImage({ url: this.data.imagePath, success: () => { my.showToast({ content: '保存成功', icon: 'success' }) }, fail: () => { my.showToast({ content: '保存失败', icon: 'error' }) } }); }
需要注意的是:
- 可使用网络图片,在组件内部已经做了downloadFile处理,对应的图片链接需在小程序后台配置合法域名。
- css为绝对定位,离左离右多少像素都可自己配置,
- 文字居中需要设置align: center; left: 为容器的一半,比如我是750rpx,这里就写375rpx;
- 文字换行实现(maxLines)只需要设置宽度,maxLines如果设置为1,那么超出一行将会展示为省略号
- 官方文档是不支持绘制圆角为:10rpx 0rpx 0rpx 0rpx 类似的效果。
如果想支持四个圆角的 试试 把Pen.js文件78行的_doClip方法重写,代码如下:
_doClip(borderRadius, width, height) { if (borderRadius && width && height) { let border = borderRadius.split(' ') let r1 = 0 let r2 = 0 let r3 = 0 let r4 = 0 if (border.length==1){ r1 = r2 = r3 = r4 = Math.min(border[0].toPx(), width / 2, height / 2); }else{ r1 = Math.min(border[0] == 0 ? 0 : border[0].toPx(), width / 2, height / 2); r2 = Math.min(border[1] == 0 ? 0 : border[1].toPx(), width / 2, height / 2); r3 = Math.min(border[2] == 0 ? 0 : border[2].toPx(), width / 2, height / 2); r4 = Math.min(border[3] == 0 ? 0 : border[3].toPx(), width / 2, height / 2); } //const r = Math.min(borderRadius.toPx(), width / 2, height / 2); // 防止在某些机型上周边有黑框现象,此处如果直接设置 setFillStyle 为透明,在 Android 机型上会导致被裁减的图片也变为透明, iOS 和 IDE 上不会 // setGlobalAlpha 在 1.9.90 起支持,低版本下无效,但把 setFillStyle 设为了 white,相对默认的 black 要好点 this.ctx.setGlobalAlpha(0); this.ctx.setFillStyle('white'); this.ctx.beginPath(); this.ctx.arc(-width / 2 + r1, -height / 2 + r1, r1, 1 * Math.PI, 1.5 * Math.PI); this.ctx.lineTo(width / 2 - r2, -height / 2); this.ctx.arc(width / 2 - r2, -height / 2 + r2, r2, 1.5 * Math.PI, 2 * Math.PI); this.ctx.lineTo(width / 2, height / 2 - r3); this.ctx.arc(width / 2 - r3, height / 2 - r3, r3, 0, 0.5 * Math.PI); this.ctx.lineTo(-width / 2 + r4, height / 2); this.ctx.arc(-width / 2 + r4, height / 2 - r4, r4, 0.5 * Math.PI, 1 * Math.PI); this.ctx.closePath(); this.ctx.fill(); // 在 ios 的 6.6.6 版本上 clip 有 bug,禁掉此类型上的 clip,也就意味着,在此版本微信的 ios 设备下无法使用 border 属性 if (!(getApp().systemInfo && getApp().systemInfo.version <= '6.6.6' && getApp().systemInfo.platform === 'ios')) { this.ctx.clip(); } this.ctx.setGlobalAlpha(1); } }
原理为:把borderRadius属性Split为多个变量,然后一个一个判断有没有值。
使用方法(要么只给一个值,要么四个值都给):
css: { borderRadius: '16rpx' borderRadius: '16rpx 0 0 16rpx' }
最后附上效果图:
下载支付宝小程序Painter包:
https://github.com/minchangyong/alipay-serverless/tree/master/client/components