仿微信圈子繪制帶小程序碼的海報(bào)(超詳細(xì)爬坑指南)

前言:在弄「新城名錄」這個(gè)小程序時(shí)美澳,需要將發(fā)布的信息內(nèi)容生成一張帶小程序碼的海報(bào)竞阐,方便分享和轉(zhuǎn)發(fā)僚害。海報(bào)的形式是參照“微信圈子”里面的樣式硫椰,折騰不了不少時(shí)間,也踩了很多坑萨蚕,故在此記錄下來靶草。

首先看看實(shí)現(xiàn)效果


點(diǎn)擊“生成海報(bào)”,預(yù)覽生成的海報(bào)然后保存至相冊

此小程序是基于uni-app開發(fā)的岳遥,也就是vue那套寫法奕翔,所以將海報(bào)的生成邏輯弄成了單獨(dú)的組件。
整個(gè)實(shí)現(xiàn)流程大致如下圖:


繪制過程

一浩蓉、初始化基本尺寸

通過wx.getSystemInfo獲得窗口信息派继。
因?yàn)閏anvas上繪制文字單位是px,所以要通過像素比來計(jì)算文字大小捻艳。

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

二、獲取小程序碼

2-1: 生成buffer

獲取小程序碼看文檔就行了沒什么可說的:傳送門
我是通過云函數(shù)獲取小程序碼的Buffer

// 云函數(shù)
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 兩個(gè)接口加起來最多可生成10萬個(gè)小程序碼讯泣。同一參數(shù)的path是一個(gè)纫普,不限請(qǐng)求次數(shù)。
wxacode.getUnlimited 是無限個(gè)小程序碼好渠,但path有限制昨稼,好像不能帶參數(shù)還沒試過。

2-2: 調(diào)用云函數(shù)獲得buffer并轉(zhuǎn)為base64

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

在開發(fā)的過程中莫名其妙的小程序碼就繪制不出來了拳锚,最后發(fā)現(xiàn)這里rs.result.buffer對(duì)象中假栓,一會(huì)有data字段一會(huì)沒有,別問為什么總之遇到了霍掺,最好判斷下匾荆。

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

這里轉(zhuǎn)換的是沒有base64前綴的拌蜘,若要顯示在 <image> 標(biāo)簽上,需要加上前綴牙丽。

2-3:獲得本地臨時(shí)鏈接

用wx.getFileSystemManager().writeFile方法寫入到本地简卧。
在繪制完成之后,通過wx.getFileSystemManager().unlink刪除臨時(shí)文件烤芦。

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

三举娩、計(jì)算畫布的高度

首先看看海報(bào)的布局



由圖可知畫布的高度=圖片區(qū)域+文字區(qū)域+小程序碼區(qū)域+邊距

3-1:計(jì)算繪制圖片所占的高度

用canvas繪制圖片,首先要將圖片下載成功后才能繪制构罗。
在使用wx.downloadFile下載圖片時(shí)铜涉,如果遇到錯(cuò)誤:downloadFile:fail url not in domain list
那么就要在小程序管理后臺(tái)中:開發(fā)>開發(fā)設(shè)置>服務(wù)器域名 去設(shè)置downloadFile合法域名
如果用到了云存儲(chǔ),合法域名就在 云開發(fā)>存儲(chǔ) 中找到文件的https的下載地址


云存儲(chǔ)的域名

圖片下載完成后遂唧,通過wx.getImageInfo來獲得圖片的尺寸芙代,然后根據(jù)數(shù)量不同采用不同的排版方式。


圖片排版方式

在這里獲取到圖片信息時(shí)盖彭,就計(jì)算好坐標(biāo)纹烹、尺寸暫存起來,等繪制的時(shí)候直接使用即可谬泌。

3-2:計(jì)算繪制文字所占的高度

繪制文字主要的問題是滔韵,canvas是沒有自動(dòng)換行的逻谦,所以要把文字一個(gè)個(gè)的取出來掌实,然后計(jì)算寬度。
小程序提供了測量文本尺寸信息的接口:CanvasContext.measureText
這個(gè)玩意呢不建議用邦马,因?yàn)樵谡鏅C(jī)測試時(shí)贱鼻,這個(gè)接口的運(yùn)算速度啊慢得要死,文字一多簡直不能玩了滋将。
后面找了個(gè)現(xiàn)成的函數(shù)來獲取文本的寬度邻悬。原文鏈接
同樣在這里獲取文字寬度信息的同時(shí),將坐標(biāo)計(jì)算好暫存起來随闽,等繪制的時(shí)候直接使用即可父丰。
最后是小程序區(qū)域的高度,是固定高度100

四掘宪、開始繪制

4-1: 獲取CanvasContext

首先創(chuàng)建 canvas 的繪圖上下文 CanvasContext 對(duì)象
注意這里要將this參數(shù)帶上
在自定義組件下蛾扇,當(dāng)前組件實(shí)例的this,表示在這個(gè)自定義組件下查找擁有 canvas-id 的 canvas 魏滚,如果省略則不在任何自定義組件內(nèi)查找

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

然后就是按順序繪制圖片镀首、文字、小程序碼區(qū)域

4-2: 導(dǎo)出圖片

繪制完成之后就需要用wx.canvasToTempFilePath鼠次,將畫布的內(nèi)容導(dǎo)出成指定大小的圖片更哄。
官方文檔說了:在 draw() 回調(diào)里調(diào)用該方法才能保證圖片導(dǎo)出成功芋齿。
理論上應(yīng)該是如下這樣子:

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

但是,這個(gè)回調(diào)它根本不執(zhí)行呀成翩!
后面查到的是說:繪制速度太快(what ???) 無法進(jìn)入canvas.draw的回調(diào)函數(shù)觅捆,需要在外層套個(gè)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傳入進(jìn)去麻敌,同時(shí)最好指定好畫布區(qū)域的寬高惠拭,不然可能存在圖片空白的情況。

4-3: 預(yù)覽圖片

繪制的<canvas>節(jié)點(diǎn)是隱藏在屏幕外的庸论,真正用于預(yù)覽的是<image>節(jié)點(diǎn)
如圖:


為什么不直接用<canvas>來預(yù)覽职辅?
因?yàn)轭A(yù)覽的尺寸和canvas的尺寸不一樣,所以就要做縮放聂示,將<canvas>標(biāo)簽用css3 transform:scale(.8, .8) 在真機(jī)上是沒有作用的S蛐!鱼喉!
所以只能把wx.canvasToTempFilePath導(dǎo)出的圖片路徑秀鞭,放到<image>上來做顯示。
但是扛禽,
導(dǎo)出來的圖片有可能存在留白區(qū)域锋边,像就是沒繪制完,這里就要如4-2所說编曼,把導(dǎo)出寫到draw回調(diào)中豆巨,同時(shí)一定要把延時(shí)加上。

五掐场、保存圖片

當(dāng)你滿懷欣喜的爬完上面的坑往扔,以為調(diào)用一下wx.saveImageToPhotosAlbum接口把圖片保存完,就大功告成了碼熊户?
不存在的F继拧!嚷堡!
此接口需要用戶授權(quán)蝗罗,才能成功將圖片保存,而如果用戶不小心點(diǎn)了拒絕授權(quán)蝌戒,那么是不是要手動(dòng)調(diào)用下跳轉(zhuǎn)到授權(quán)設(shè)置頁面串塑。

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

跳轉(zhuǎn)授權(quán)?跳轉(zhuǎn)得了屁勒瓶颠!
翻看wx.openSetting的官方文檔拟赊,有那么一小撮字告訴你:用戶發(fā)生點(diǎn)擊行為后,才可以跳轉(zhuǎn)打開設(shè)置頁
所以你還得整個(gè)對(duì)話框粹淋,讓用戶點(diǎn)一下子吸祟。

wx.getSetting({
    success(res) {
        // 進(jìn)行授權(quán)檢測瑟慈,未授權(quán)則進(jìn)行彈層授權(quán)
        if (!res.authSetting['scope.writePhotosAlbum']) {
            wx.authorize({
                scope: 'scope.writePhotosAlbum',
                // 拒絕授權(quán)時(shí),則進(jìn)入手機(jī)設(shè)置頁面屋匕,可進(jìn)行授權(quán)設(shè)置
                fail(err) {
                    wx.showModal({
                        title: '提示',
                        content: '需要您授權(quán)才能保存到相冊',
                        success: (res) => {
                            if (res.confirm) {
                                wx.openSetting({
                                  ...
                                })
                            }
                        }
                    })
                }
            })
        } 
    }
})

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末葛碧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子过吻,更是在濱河造成了極大的恐慌进泼,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纤虽,死亡現(xiàn)場離奇詭異乳绕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)逼纸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門洋措,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杰刽,你說我怎么就攤上這事菠发。” “怎么了贺嫂?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵滓鸠,是天一觀的道長。 經(jīng)常有香客問我第喳,道長糜俗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任墩弯,我火速辦了婚禮吩跋,結(jié)果婚禮上寞射,老公的妹妹穿的比我還像新娘渔工。我一直安慰自己,他們只是感情好桥温,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布引矩。 她就那樣靜靜地躺著,像睡著了一般侵浸。 火紅的嫁衣襯著肌膚如雪旺韭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天掏觉,我揣著相機(jī)與錄音区端,去河邊找鬼。 笑死澳腹,一個(gè)胖子當(dāng)著我的面吹牛织盼,可吹牛的內(nèi)容都是我干的杨何。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼沥邻,長吁一口氣:“原來是場噩夢啊……” “哼危虱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起唐全,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤埃跷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后邮利,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弥雹,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年延届,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缅糟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祷愉,死狀恐怖窗宦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情二鳄,我是刑警寧澤赴涵,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站订讼,受9級(jí)特大地震影響髓窜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜欺殿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一寄纵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脖苏,春花似錦程拭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亦歉,卻和暖如春恤浪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肴楷。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國打工水由, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赛蔫。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓砂客,卻偏偏與公主長得像直秆,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鞭盟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361