仿微信圈子绘制带小程序码的海报(超详细爬坑指南)

前言:在弄「新城名录」这个小程序时,需要将发布的信息内容生成一张带小程序码的海报,方便分享和转发。海报的形式是参照“微信圈子”里面的样式,折腾不了不少时间,也踩了很多坑,故在此记录下来。

首先看看实现效果


点击“生成海报”,预览生成的海报然后保存至相册

此小程序是基于uni-app开发的,也就是vue那套写法,所以将海报的生成逻辑弄成了单独的组件。
整个实现流程大致如下图:


绘制过程

一、初始化基本尺寸

通过wx.getSystemInfo获得窗口信息。
因为canvas上绘制文字单位是px,所以要通过像素比来计算文字大小。

let _ = this
wx.getSystemInfo ({
    success (res) {
        _.windowInfo.width = _.canvasStyle.width = res.windowWidth
        _.windowInfo.height = res.windowHeight
        _windowInfo.ratio = res.pixelRatio / 2
        // 根据像素比,计算文字的大小
        _.canvasStyle.textDf *= _windowInfo.ratio
        _.canvasStyle.textSm *= _windowInfo.ratio
    }
})

二、获取小程序码

2-1: 生成buffer

获取小程序码看文档就行了没什么可说的:传送门
我是通过云函数获取小程序码的Buffer

// 云函数
app.router('wxacode', async (ctx, next) => {
    try {
        let rs = await cloud.openapi.wxacode.get({
            path: event.path,
            width: 100,
            is_hyaline: true
        })
        ctx.body = rs
     } catch (err) {
        ctx.body = err
     }
}) 

wxacode.createQRCodewxacode.get 两个接口加起来最多可生成10万个小程序码。同一参数的path是一个,不限请求次数。
wxacode.getUnlimited 是无限个小程序码,但path有限制,好像不能带参数还没试过。

2-2: 调用云函数获得buffer并转为base64

wx.cloud.callFunction ({
     ...
}).then(rs => {
    let base64 = wx.arrayBufferToBase64(rs.result.buffer.data || rs.result.buffer)
})

在开发的过程中莫名其妙的小程序码就绘制不出来了,最后发现这里rs.result.buffer对象中,一会有data字段一会没有,别问为什么总之遇到了,最好判断下。

let imgSrc = 'data:image/jpeg;base64,'  + base64

这里转换的是没有base64前缀的,若要显示在 <image> 标签上,需要加上前缀。

2-3:获得本地临时链接

用wx.getFileSystemManager().writeFile方法写入到本地。
在绘制完成之后,通过wx.getFileSystemManager().unlink删除临时文件。

let filePath = `${wx.env.USER_DATA_PATH}/wxacode.jpeg`
wx.getFileSystemManager().writeFile({
    filePath,
    data,
    encoding: 'base64'
    ...
 })

三、计算画布的高度

首先看看海报的布局



由图可知画布的高度=图片区域+文字区域+小程序码区域+边距

3-1:计算绘制图片所占的高度

用canvas绘制图片,首先要将图片下载成功后才能绘制。
在使用wx.downloadFile下载图片时,如果遇到错误:downloadFile:fail url not in domain list
那么就要在小程序管理后台中:开发>开发设置>服务器域名 去设置downloadFile合法域名
如果用到了云存储,合法域名就在 云开发>存储 中找到文件的https的下载地址


云存储的域名

图片下载完成后,通过wx.getImageInfo来获得图片的尺寸,然后根据数量不同采用不同的排版方式。


图片排版方式

在这里获取到图片信息时,就计算好坐标、尺寸暂存起来,等绘制的时候直接使用即可。

3-2:计算绘制文字所占的高度

绘制文字主要的问题是,canvas是没有自动换行的,所以要把文字一个个的取出来,然后计算宽度。
小程序提供了测量文本尺寸信息的接口:CanvasContext.measureText
这个玩意呢不建议用,因为在真机测试时,这个接口的运算速度啊慢得要死,文字一多简直不能玩了。
后面找了个现成的函数来获取文本的宽度。原文链接
同样在这里获取文字宽度信息的同时,将坐标计算好暂存起来,等绘制的时候直接使用即可。
最后是小程序区域的高度,是固定高度100

四、开始绘制

4-1: 获取CanvasContext

首先创建 canvas 的绘图上下文 CanvasContext 对象
注意这里要将this参数带上
在自定义组件下,当前组件实例的this,表示在这个自定义组件下查找拥有 canvas-id 的 canvas ,如果省略则不在任何自定义组件内查找

let ctx = wx.createCanvasContext ('mycanvas', this)

然后就是按顺序绘制图片、文字、小程序码区域

4-2: 导出图片

绘制完成之后就需要用wx.canvasToTempFilePath,将画布的内容导出成指定大小的图片。
官方文档说了:在 draw() 回调里调用该方法才能保证图片导出成功。
理论上应该是如下这样子:

ctx.draw(true, () =>  {
    wx.canvasToTempFilePath({ ... })
})

但是,这个回调它根本不执行呀!
后面查到的是说:绘制速度太快(what ???) 无法进入canvas.draw的回调函数,需要在外层套个setTimeout。

let _ = this
ctx.draw(true, setTimeout(() => {
    wx.canvasToTempFilePath ({
        fileType: 'jpg',
        canvasId: 'mycanvas',
        x: 0,
        y: 0,
        width: _.canvasStyle.width,
        height: _.canvasStyle.height,
        success (res) {
              _.imgSrc = res.tempFilePath
        }
    }, _)
}, 300))

在使用wx.canvasToTempFilePath记得把this传入进去,同时最好指定好画布区域的宽高,不然可能存在图片空白的情况。

4-3: 预览图片

绘制的<canvas>节点是隐藏在屏幕外的,真正用于预览的是<image>节点
如图:


为什么不直接用<canvas>来预览?
因为预览的尺寸和canvas的尺寸不一样,所以就要做缩放,将<canvas>标签用css3 transform:scale(.8, .8) 在真机上是没有作用的!!!
所以只能把wx.canvasToTempFilePath导出的图片路径,放到<image>上来做显示。
但是,
导出来的图片有可能存在留白区域,像就是没绘制完,这里就要如4-2所说,把导出写到draw回调中,同时一定要把延时加上。

五、保存图片

当你满怀欣喜的爬完上面的坑,以为调用一下wx.saveImageToPhotosAlbum接口把图片保存完,就大功告成了码?
不存在的!!!
此接口需要用户授权,才能成功将图片保存,而如果用户不小心点了拒绝授权,那么是不是要手动调用下跳转到授权设置页面。

wx.getSetting({
    success(res) {
        if (!res.authSetting['scope.writePhotosAlbum']) {
            wx.authorize({
                scope: 'scope.writePhotosAlbum',
                ...
                fail () {
                    wx.openSetting(...)
                }
            })
        } 
    }
})

跳转授权?跳转得了屁勒!
翻看wx.openSetting的官方文档,有那么一小撮字告诉你:用户发生点击行为后,才可以跳转打开设置页
所以你还得整个对话框,让用户点一下子。

wx.getSetting({
    success(res) {
        // 进行授权检测,未授权则进行弹层授权
        if (!res.authSetting['scope.writePhotosAlbum']) {
            wx.authorize({
                scope: 'scope.writePhotosAlbum',
                // 拒绝授权时,则进入手机设置页面,可进行授权设置
                fail(err) {
                    wx.showModal({
                        title: '提示',
                        content: '需要您授权才能保存到相册',
                        success: (res) => {
                            if (res.confirm) {
                                wx.openSetting({
                                  ...
                                })
                            }
                        }
                    })
                }
            })
        } 
    }
})

最后附上完整代码地址:https://github.com/yiPian/poster

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342