绘制分享海报要注意的点
业务上经常存在一种场景就是需动态的绘制出一张漂亮的海报,并且能支持到把海报分享给朋友或者朋友圈的操作。今天就简单来聊聊怎么实现动态绘制海报并保存分享图片的功能。
首先,选用Qrcode插件来实现动态生成二维码,由于小程序的限制,这边实现直接通过把qrcode源码放到项目里,用自定义utils的方式来引入。
接下来就实现绘制方法:
Tips: 微信小程序canvas有两套API,大家按照自己的需求使用,本文使用的是老的接口;
<!-- 因为小程序同层渲染和原生组件的坑,用wxss设置样式使视窗不可见会导致绘制不出来,但是用内联样式可以实现 -->
<canvas
wx:if="{{!posterImage}}"
class="poster-canvas"
id="poster"
canvas-id="poster"
style="width:{{standardWidth}}px;height:{{standardHeight}}px;position:fixed;left:2000px;top:0;"
/>
<!-- 仅仅用做于绘制qrcode载体,不需要显示 -->
<canvas
wx:if="{{!posterImage}}"
id="qrcode"
canvas-id="qrcode"
class="qrcode-canvas"
style="width:{{qrCodeWidth}}px;height:{{qrCodeHeight}}px;position:fixed;left:2000px;top:0;"
/>
用两个元素的原因是因为绘制过程中经常会出现位置偏移的问题,这边通过占位元素和定位来实现位置固定;
const { ctx } = await this.getCtx('poster')
// 获取canvas元素
getCtx (selector) {
return new Promise((resolve) => {
const ctx = wx.createCanvasContext(selector, this)
const { standardWidth, standardHeight } = this.properties
resolve({
ctx,
width: standardWidth,
height: standardHeight,
})
})
}
// 绘制二维码
drawQrCode (canvasNodeRef) {
const { ctx } = canvasNodeRef
const { qrCode } = this.data.jsonData
const { standardRate, shareLink } = this.data
return new Promise(async (resolve, reject) => {
QrCode.api.draw(shareLink, 'qrcode', qrCode.width, qrCode.height, this)
// Qrcode.api.draw的坑,接口流程已经完成,但是实际上并没有绘制完成(视窗不可见),所以通过一些延迟来确保大部分都已经绘制完成
await this.delay()
wx.canvasToTempFilePath({
canvasId: 'qrcode',
success: async (res) => {
ctx.drawImage(
res.tempFilePath,
qrCode.x * standardRate,
qrCode.y * standardRate,
qrCode.width * standardRate,
qrCode.height * standardRate
)
resolve(res.tempFilePath)
},
fail: (err) => {
reject(err)
}
}, this)
})
}
绘制的过程中需要注意处理一下绘制的图片资源
getTransImg (imgUrl) {
const newUrl = imgUrl.replace('http://', 'https://')
return new Promise((resolve) => {
this.downLoadPicSource(newUrl).then(async (res) => {
resolve(res.tempFilePath)
}).catch(e => {
console.log('资源下载失败: ', e.message, newUrl)
})
})
}
downLoadPicSource (url) {
const newUrl = url.replace('http://', 'https://')
return downloadSource({ url: newUrl })
}
这时候几本就把二维码绘制完成了,如果需要绘制其他元素可以自己按需添加,以下举例几种:
// 绘制背景
const bgUrl = await this.getTransImg(pictureUrl)
ctx.drawImage(bgUrl, 0, 0, standardWidth, standardHeight)
// 绘制logo/头像
const { ctx } = canvasNodeRef
const { logo } = this.data.jsonData
const { standardRate } = this.data
const logoUrl = await this.getTransImg(this.properties.logoUrl)
ctx.drawImage(
logoUrl,
logo.x * standardRate,
logo.y * standardRate,
logo.width * standardRate,
logo.height * standardRate,
)
绘制完成之后需要保存海报图,需要注意保存图片失真的问题,需要根据当前系统的dpx做一些处理
ctx.draw(true, () => {
// 绘制完成 - 把canvas转化为图片
const dpr = wx.getSystemInfoSync().pixelRatio
const picW = this.properties.standardWidth
const picH = this.properties.standardHeight
wx.canvasToTempFilePath({
canvasId: 'poster',
width: picW,
height: picH,
destWidth: picW * dpr,
destHeight: picH * dpr,
success: (res) => {
this.setData({
inDrawing: false,
posterImage: res.tempFilePath,
})
this.triggerEvent('drawEnd', res.tempFilePath)
},
fail: (e) => {
this.drawError('生成海报失败', e)
}, this)
}
绘制完成之后,只要把海报图回显到页面中即可。接下来就只要实现长按分享或者保存就可以实现了
// 判断分享图片功能是否可用
if (!wx.canIUse('showShareImageMenu')) {
// 把图片保存到相册
wx.saveImageToPhotosAlbum({
filePath: this.data.posterImage,
success: (evt) => {
console.log('保存成功: ', evt)
wx.showToast({
title: '保存成功'
})
},
fail: (err) => {
console.log('保存失败了: ', err)
wx.showToast({
title: '保存失败',
icon: 'none'
})
}
})
return
}
// 分享海报
wx.showShareImageMenu({
path: this.data.posterImage,
success: (res) => {
console.log('分享成功', res)
wx.showToast({
title: '操作成功',
})
},
fail: (err) => {
console.log('分享失败', err)
// 取消的情况不弹窗提醒
if (err.errMsg.indexOf('cancel') !== -1) {
return
}
wx.showToast({
title: `分享失败:${err.errMsg}`,
icon: 'none'
})
}
})
大功告成!