您當前位置>首頁 » 新聞資訊 » 小(xiǎo)程序相(xiàng)關 >
小(xiǎo)程序如(rú)何生(shēng)成海(hǎi)報(bào±®σ)分(fēn)享朋(péng)友(yǒu)圈
發表時(shí)間(jiān):2021-1-5
發布人(rén):葵宇科(kē)技(jì)
浏覽次數(shù):54
項目需求寫完有(yǒu)一(yī)段時(shí)間(jiā↓☆₹<n)了(le),但(dàn)是(shì)還(hái)是(shì)想回過來(lái)總結≠一(yī)下(xià),一(yī)是(shì)對(duì)項目的(de)回顧ασ≤→優化(huà)等,二是(shì)對(duì)坑的(de)地(dì)方做(zuò)個(gè≤γ↓)記錄,避免以後遇到(dào)類似的(de)問(wèn)題。 §<
需求
利用(yòng)微(wēi)信強大(dà)的(de)社交能(n₽ ≤"éng)力通(tōng)過小(xiǎo)程序達到(dàσΩo)裂變的(de)目的(de),拉取新用(yòng)戶。
生(shēng)成的(de)海(hǎi)報(bào)如(rú)下(xià)
需求分(fēn)析
1、利用(yòng)小(xiǎo)程序官方提供的(de)api可(kě)以直接分(fēn)享轉發到 ¥↕≤(dào)微(wēi)信群打開(kāi)小(xiǎo)程序
2、利用(yòng)小(xiǎo)程序生(shēng)成海(hǎi)報(b↓ào)保存圖片到(dào)相(xiàng)冊分(fēn)享到(dào)朋(péng)友(y♥¥ǒu)圈,用(yòng)戶長(cháng)按識别二維碼關注公衆号或者打¥↕∏©開(kāi)小(xiǎo)程序來(lái)達到(dào)裂變的(de)±目的(de)
實現(xiàn)方案
一(yī)、分(fēn)析如(rú)何實現(xiàn)
相(xiàng)信大(dà)家(jiā)應該都(dōu)會(huì)有(yǒu)類似的(d>☆★∏e)迷惑,就(jiù)是(shì)如(rú)何按照(zhào&λ¥π)産品設計(jì)的(de)那(nà)樣繪制(zhì)成海(hσ¶∞↕ǎi)報(bào),其實當時(shí)我也(yě)是(shì)不(bù)知(zhī)道<₽(dào)如(rú)何下(xià)手,認真想了(le)★ ♦←下(xià)得(de)通(tōng)過canvas繪制(zhì)成圖片,這(zhè☆>εε)樣用(yòng)戶保存這(zhè)個(gè)圖片到(dào)相(xiàng)冊,就 γ(jiù)可(kě)以分(fēn)享到(dào)朋(péng)友(yǒu)圈了(le)。但(dàλ↓₩&n)是(shì)要(yào)繪制(zhì)的(de)¥£∏★圖片上(shàng)面不(bù)僅有(yǒu)文(wén)字還(hái)有(yǒu)數(★↔®₩shù)字、圖片、二維碼等且都(dōu)是(shì)活的(de),這(zhè)個(gè)要(<↔yào)怎麽動态生(shēng)成呢(ne)。認真想了(le)下(xià),需要""Ω(yào)一(yī)點一(yī)點的(de)将文(wén)字和(h÷>é)數(shù)字,背景圖繪制(zhì)到(dào)畫(huà)布上(shàng)σε&去(qù),這(zhè)樣通(tōng)過api最終合成一(yī)個("♠gè)圖片導出到(dào)手機(jī)相(xiàng↑&γ )冊中。
二、需要(yào)解決的(de)問(wèn)題
1、二維碼的(de)動态獲取和(hé)繪制(zhì)(包€™括如(rú)何生(shēng)成小(xiǎo)程序二維碼、公衆号二維碼、打開(kāi)§ 網頁二維碼)
2、背景圖如(rú)何繪制(zhì),獲取圖片信息
3、将繪制(zhì)完成的(de)圖片保存到(dào)本地φ✘$☆(dì)相(xiàng)冊
4、處理(lǐ)用(yòng)戶是(shì)否取消授權保存到(dào)相(xiàng)冊
三、實現(xiàn)步驟
這(zhè)裡(lǐ)我具體(tǐ)寫下(xià)圍繞上(shàng)面所提出的(de)問(wβ₹λèn)題,描述大(dà)概實現(xiàn)的(de)過程
①首先創建canvas畫(huà)布,我把畫(huà♣∑$✔)布定位設成負的(de),是(shì)為(wèi)了(le)不(bù)讓它顯示在頁面♠π上(shàng),是(shì)因為(wèi)我嘗試把canvas通(tōng)過判斷條件π©(jiàn)動态的(de)顯示和(hé)隐藏,在繪制(zhì)的(de)時(©λ↔★shí)候會(huì)出現(xiàn)問(wèn)題,所以采用&$(yòng)了(le)這(zhè)種方法,這(zhè)裡 γ(lǐ)還(hái)有(yǒu)一(yī)定要(yào)設置畫(huà)布的(de)大(dà)小≥♥β≠(xiǎo)。
<canvas canvas-id="myCanvas" style="width: 690px;height:1085px;position: f¶φ×ixed;top: -10000px;"></canvas>
②創建好(hǎo)畫(huà)布之後,先繪制(zhì)背景圖,因為(wèi)背景圖我是(shìδ↑≠∑)放(fàng)在本地(dì),所以獲取 <canvas> 組件(ji♠ ✔àn) canvas-id 屬性,通(tōng)過createCanvasContextφ₹>↓創建canvas的(de)繪圖上(shàng)下(xià)文(wén) C ×βanvasContext 對(duì)象。使用(yòng)dδ≠←₽rawImage繪制(zhì)圖像到(dào)畫(huà)布,第一(yī)個(gè)參數÷&(shù)是(shì)圖片的(de)本地(dì)地(dì)址"✘β,後面兩個(gè)參數(shù)是(shì)圖像相(xiàng)對(duì)畫αφ(huà)布左上(shàng)角位置的(de)x軸和(hé)y軸,最後兩個(gè)參數(shù)是↕ β(shì)設置圖像的(de)寬高(gāo)。
const ctx = wx.createCanvasContext('myCanvas')
ctx.drawImage('/img/study/shareimg.png', 0, 0, 690, 1085)
③創建好(hǎo)背景圖後,在背景圖上(shàng)繪制(zhì)頭像,文(wén)γ✔↔§字和(hé)數(shù)字。通(tōng)過getImageInfo獲取頭像的(de)信息€©€,這(zhè)裡(lǐ)需要(yào)注意下(xià)在獲取的(de)網絡圖片要(yào)×¶←先配置download域名才能(néng)生(shēng)效,具體(tǐ)在小(xiǎ↕o)程序後台設置裡(lǐ)配置。
獲取頭像地(dì)址,首先量取頭像在畫(huà)布中的(de)大(dà↔←↔ )小(xiǎo),和(hé)x軸Y軸的(de)坐(zuò)标,這(zhè)裡(lǐ)的(de✘↕™)result[0]是(shì)我用(yòng)promise封裝返回的(de)一(y$σ♠ī)個(gè)圖片地(dì)址
let headImg = new Promise(function (resolve) {
wx.getImageInfo({
src: `${app.globalData.baseUrl2}${that.data.currentChildren.headImg}`,
success: function (res) {
resolve(res.path)
},
fail: function (err) {
console.log(err)
wx.showToast({
title: '網絡錯(cuò)誤請(qǐng)重試',
icon: 'loading'
})
}
})
})
let avatarurl_width = 60, //繪制(zhì)的(de)頭像寬度
avatarurl_heigth = 60, //繪制(zhì)的(de)頭像高(gāo)度
avatarurl_x = 28, //繪制(zhì)δ∏的(de)頭像在畫(huà)布上(shàng)的(de)位置
avatarurl_y = 36; //繪制(zhì)的(de)頭像在畫(huà)≥₽Ω布上(shàng)的(de)位置
ctx.save(); // 先保存狀态 已便于畫(huà)完圓✔$☆再用(yòng)
ctx.beginPath(); //開(kāi)始繪制(zhì)
//先畫(huà)個(gè)圓 前兩個(gè)參數(shù)确定了(le)圓心 (x,y≠↔) 坐(zuò)标 第三個(gè)參數(shù)是(s→∑₹&hì)圓的(de)半徑 四參數(shù)是(shì)繪圖方向 默認±λ是(shì)false,即順時(shí)針
ctx.arc(avatarurl_width / 2 + ↔↔avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, ©₩§avatarurl_width / 2, 0, Math.♥≈PI * 2, false);
ctx.clip(); //畫(huà)了(le)圓 再剪切 原始畫(huλ∑à)布中剪切任意形狀和(hé)尺寸。一(yī)旦剪切了(¶<$le)某個(gè)區(qū)域,則所有(yǒu)之後的(de)繪圖都(dōu)會(huì)被>β限制(zhì)在被剪切的(de)區(qū)域內(nèi)
ctx.drawImage(result[0], avatarurl"δ•€_x, avatarurl_y, avatarurl_width, avatarurlπ₩♣_heigth); // 推進去(qù)圖片
這(zhè)裡(lǐ)舉個(gè)例子(zǐ)說(shuō)下(xià)如(✘✔€rú)何繪制(zhì)文(wén)字,比如(rú)我要(δ∞¥¶yào)繪制(zhì)如(rú)下(xià)這(zhè)個(gè)“字”,需要(yà↑¥©♠o)動态獲取前面字數(shù)的(de)總寬度,這(z✔↔ε>hè)樣才能(néng)設置“字”的(de)x軸坐(zuò)标,這(zh✔↕λè)裡(lǐ)我本來(lái)是(shì)想通(tōng)¶σ≥←過measureText來(lái)測量字體(tǐ)的(de)寬度,但(dà♦↔β→n)是(shì)在iOS端第一(yī)次獲取的(de)寬度值不(bùλ∏✘')對(duì),關于這(zhè)個(gè)問(wèn)題,我還£λ✔¥(hái)在微(wēi)信開(kāi)發者社區(qū)提了(le)bug,所以我✔γ想用(yòng)另一(yī)個(gè)方法來(lái)實現(xiàn),就(j≠×₽iù)是(shì)先獲取正常情況下(xià)一(yī)個(∏ ¥gè)字的(de)寬度值,然後乘以總字數(shù)就(ji™→®ù)獲得(de)了(le)總寬度,親試是(shì)可(kě)以的>∞♠(de)。

let allReading = 97 / 6 / app.globalData.ratio * wordNumber.©π toString().length + 325;
ctx.font = 'normal normal 30px sans-serif';
ctx.setFillStyle('#ffffff')
ctx.fillText('字', allReading, 150);
④繪制(zhì)公衆号二維碼,和(hé)獲取頭像是(shì)一(yī)樣的(de),也(yěδ↕←)是(shì)先通(tōng)過接口返回圖片網絡地(≥♠εdì)址,然後再通(tōng)過getImageInfo•π±β獲取公衆号二維碼圖片信息
⑤如(rú)何繪制(zhì)小(xiǎo)程序碼,具體(tǐ)官網文(wé§↔→n)檔也(yě)給出生(shēng)成無限小(xiǎo)程序碼接口,∞©'通(tōng)過生(shēng)成的(de)小(xiǎo)程序可(kě)以打開(kāi)任意φε>一(yī)個(gè)小(xiǎo)程序頁面,并且二維ε₽↓碼永久有(yǒu)效,具體(tǐ)調用(yòng)哪個(gè)小(xiǎo)程序二λ↓☆維碼接口有(yǒu)不(bù)同的(de)應用(yòng)場(chǎng)景,具體(tǐ)©↓可(kě)以看(kàn)下(xià)官方文(wén)ε≠φ檔怎麽說(shuō)的(de),也(yě)就(jiù)是(shì)說(shuō)前端通₩(tōng)過傳遞參數(shù)調取後端接口返回的(de)小(€™xiǎo)程序碼,然後繪制(zhì)在畫(huà)布上(shàng)(和(hα ©♦é)上(shàng)面寫的(de)繪制(zhì)頭像和π<<"(hé)公衆号二維碼一(yī)樣的(de))
ctx.drawImage('小(xiǎo)程序碼的(de)本地(dì)地(dì)址', x軸, Y軸, 寬, 高(gāo))
⑥最終繪制(zhì)完把canvas畫(huà)布轉成圖片并返回圖片★©地(dì)址
wx.canvasToTempFilePath({
canvasId: 'myCanvas',
success: function (res) {
canvasToTempFilePath = res.tem≤♦pFilePath // 返回的(de)圖片地(dì)址保存到(dào)一(yī)個(gè)γα&<全局變量裡(lǐ)
that.setData({
showShareImg: true
})
wx.showToast({
title: '繪制(zhì)成功',
})
},
fail: function () {
wx.showToast({
title: '繪制(zhì)失敗',
})
},
complete: function () {
wx.hideLoading()
wx.hideToast()
}
})
⑦保存到(dào)系統相(xiàng)冊;先判斷用(yòng)戶是(shì)™€®否開(kāi)啓用(yòng)戶授權相(xiàng)冊,處理(lǐ)不(bù)同情況下(xià€₽)的(de)結果。比如(rú)用(yòng)戶如(rú)果按照(zhào)正常邏輯授權是∏α∑(shì)沒問(wèn)題的(de),但(dàn)是(s$hì)有(yǒu)的(de)用(yòng)戶如(rú)果點擊了(le)取消授權€₽←該如(rú)何處理(lǐ),如(rú)果不(bù)處理≥≠♣★(lǐ)會(huì)出現(xiàn)一(yī)定的(de)問(wèn)題。所以當用(α↕yòng)戶點擊取消授權之後,來(lái)個(gè)彈框提示,當它再∞α次點擊的(de)時(shí)候,主動跳(tiào)到(d ★ào)設置引導用(yòng)戶去(qù)開(kāi)啓授權,從(c÷∞§óng)而達到(dào)保存到(dào)相(xiàng)冊分(fēn₽λ×)享朋(péng)友(yǒu)圈的(de)目的(de)。
// 獲取用(yòng)戶是(shì)否開(kāi)啓用(yò≥$ng)戶授權相(xiàng)冊
if (!openStatus) {
wx.openSetting({
success: (result) => {
if (result) {
if (result.authSetting["scope.writePhotosAlbum"] === true) {
openStatus = true;
wx.saveImageToPhotosAlbum(≠≤{
filePath: canvasToTempFilePath,
success() {
that.setData({
showShareImg: false
})
wx.showToast({
title: '圖片保存成功,快(kuài)去(qù)分(fēn)享到(dào)朋(péng)友(yǒ₩≥u)圈吧(ba)~',
icon: 'none',
duration: 2000
})
},
fail() {
wx.showToast({
title: '保存失敗',
icon: 'none'
})
}
})
}
}
},
fail: () => { },
complete: () => { }
});
} else {
wx.getSetting({
success(res) {
// 如(rú)果沒有(yǒu)則獲取授權
if (!res.authSetting['scope.writePhotosAlbum']) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success() {
openStatus = true
wx.saveImageToPhotosAlbum({
filePath: canvasToTempFilePath,
success() {
that.setData({
showShareImg: false
})
wx.showToast({
title: '圖片保存成功,快(kuài)去(qù)分(fēn)享到(dào)朋(péng)友(yǒu)圈₽π吧(ba)~',
icon: 'none',
duration: 2000
})
},
fail() {
wx.showToast({
title: '保存失敗',
icon: 'none'
})
}
})
},
fail() {
// 如(rú)果用(yòng)戶拒絕過或沒有(yǒu)授權,則再次打開(kā ∞♣i)授權窗(chuāng)口
openStatus = false
console.log('請(qǐng)設置允許訪問(wèn)相(xiàng)冊')
wx.showToast({
title: '請(qǐng)設置允許訪問(wèn)相(xiàng)冊',
icon: 'none'
})
}
})
} else {
// 有(yǒu)則直接保存
openStatus = true
wx.saveImageToPhotosAlbum({
filePath: canvasToTempFileΩ$Path,
success() {
that.setData({
showShareImg: false
})
wx.showToast({
title: '圖片保存成功,快(kuài)去(qù)分(fēn)享到(dàλ→o)朋(péng)友(yǒu)圈吧(ba)~',
icon: 'none',
duration: 2000
})
},
fail() {
wx.showToast({
title: '保存失敗',
icon: 'none'
})
}
})
}
},
fail(err) {
console.log(err)
}
})
}
總結
至此所有(yǒu)的(de)步驟都(dōu)已實現(xiàn),在繪制(zhì)的(de)時(sβ♦£hí)候會(huì)遇到(dào)一(yī)些(xiē)異步請(qǐng)求後台返回的(<↕de)數(shù)據,所以我用(yòng)promise和(hé)async和(hé)awa✔"ε§it進行(xíng)了(le)封裝,确保導出的(de)圖片信息是(shì)完整的(de↑÷ α)。在繪制(zhì)的(de)過程确實遇到(dào)一♦ε∞(yī)些(xiē)坑的(de)地(dì)方。比如(rú)初開(kāi)始導出•¥$的(de)圖片比例大(dà)小(xiǎo)不(bù)對(duì),還(hái)有(yǒu)用(y₽™òng)measureText測量文(wén)字寬度不(bù φ×)對(duì),多(duō)次繪制(zhì)(可(kě)能(néng)受網 φ♠絡原因)有(yǒu)時(shí)導出的(de)圖片上(sh♠'€εàng)的(de)文(wén)字顔色會(huì)有(yǒu)誤÷™差等。如(rú)果你(nǐ)也(yě)遇到(dào)一(yī)些(xiē)比較坑的(de)地β (dì)方可(kě)以一(yī)起探討(tǎo)下(xià)做(zuò)個(gè)記錄