前言
小程序原先转发给别人,要不是一张图片,要不是就是截屏,不是很自定义化。
我们要每个用户在不同的页面,转发的内容都不一样,这当然可以直接从服务端实时生成这样 5:4 的图片做转发,毕竟好处是避免了兼容性问题
本示例将会用另外一种思路,从客户端实时生成分享图片,并进行转发。
技术调查
微信小程序中 以 wx.createCanvasContext
为代表的 CanvasContext
(v1)
都从基础库 2.9.0
开始,停止维护了
所以我们就使用更加贴近 mdn 上 Canvas
来代替 (v2)
这里简称停止维护的版本为 v1, Canvas
版本为 v2
值得注意的是
v2 版本中还有一个 wx.createOffscreenCanvas
它也可以使用 OffscreenCanvas.getContext('2d')
这样的 api
不过它的最低版本为 2.16.1
到今天 (2021/05/05) 大部分人用的版本还是 2.16.1
(2.17.0
还在灰度)
所以说,从兼容性的角度出发, 使用 OffscreenCanvas
是有很大风险的
而且我出于好奇实验使用了一下,bug 也是一大堆 issue
开始吧
ps: canvas api 不熟的
mdn
上看看
绘图前言
- rpx 这种单位换算问题 ,
wx.getSystemInfoSync
获取一下屏幕宽,做个换算就行 - 小程序中的 canvas 2d 不能把它设置 style:display:none ,不然转化成图片时会空白 , 我们可以把它移出可视界面。
- 层级问题,canvas 和 svg 一样都是后面的覆盖前面的,所以我们可以使用 对
zIndex
进行排序,来决定每个function
的执行顺序
绘图 ing
圆角矩形可以使用 `` arc `` api 来解决
圆角图片可以使用 `` clip `` + `` drawImage ``
比如在这里因为我们需要使用微信的头像
所以需要把 图片的域名 在后台配置一下
这里贴一段 uni 下,在 ctx 中绘制圆角头像的代码
async addAvatar() {
// 下载到本地
const [err, res] = await uni.getImageInfo({
src: avatarUrl
})
const { path, type } = res
const img = canvas.createImage()
img.src = path
// 直接设置 width height 是不生效
const offsetX = 20 * dpr
const offsetY = 20 * dpr
const r = (50 * dpr) / 2
const circle = {
x: offsetX + r,
y: offsetY + r,
r: r
}
// 这样写是因为最后绘制的时候是执行一个 ()=> Promise<any>
// 这样可以确保生成的时候,图片已经 onload了
await new Promise((resolve, reject) => {
img.onload = () => {
ctx.save()
// 切圆角
ctx.beginPath()
ctx.arc(circle.x, circle.y, circle.r, Math.PI * 2, false)
ctx.clip()
// 画成指定的长和高
ctx.drawImage(img, offsetX, offsetY, 50 * dpr, 50 * dpr)
ctx.restore()
resolve()
}
img.onerror = event => {
reject(event)
}
})
}
图片的丰富度 -> svg 的引入
在上面,我们已经把图片的,渐变,排版,文字,依赖的图片全部绘制上去了
然而这时候我们想加一些丰富度,但是不想使用本地图片去那种,缝合的事情,毕竟这样也有失水准。
这时候我们自然而然的想到了 SVG-to-canvas parser
目前在 npm
主要有 fabric
/ canvg
这些
这里我选用的是 基于 canvg
的 svg2canvas
这样我们在 iconfont
就可以挑选一些 svg
下载下来,然后通过 converter
就可以直接转换成 canvas js code
使用了。
当然,转化实际上是不完美的,定位和大小,我们可以改造绘制的 draw function
在其中加入
ctx.translate(x, y)
ctx.scale(dpr / 2, dpr / 2)
颜色什么当然都可以作为参数
这样,我们的 svg
就顺利的加了上去
onShareAppMessage 转发
onShareAppMessage
是支持 promise
的,不过 promise
三秒内不 resolve , 会自动 fallback
这时候,我们就可以写出下列代码
onShareAppMessage() {
const createPromise = async () => {
try {
this.shareBtnLoading = true
// 获得图片的临时路径
const loaclPath = await aWayToGetCanvasTempFilePath()
return {
title: 'hello, i am icebreaker',
path: 'resolve path',
imageUrl: loaclPath
}
} catch (e) {
console.warn(e)
} finally {
this.shareBtnLoading = false
}
}
return {
title: 'fallback title',
promise: createPromise(),
path: 'fallback path',
imageUrl: 'fallback imageUrl'
}
}
获得图片的临时路径,可以使用 wx.canvasToTempFilePath
api
这个api是 画布 v1 版本 和 v2版本通用的
不过 v1 版本传入的是 canvas-id
v2 则是 canvas
组件实例
生成速度
在不同的 dpr 下,时间都很快 真机在 300ms
左右 远小于 3000ms
自动 fallback 的限制
效果视频
在知乎上, 链接地址