微信小程序踩坑記錄 ------- canvas 生成帶小程序碼的微信朋友圈分享圖

最近做了一個(gè)問卷類的小程序纳账,其中的結(jié)果頁想讓用戶進(jìn)行朋友圈分享轉(zhuǎn)發(fā)幅聘,網(wǎng)上搜索資料,得出解決思路余寥,用 canvas 將頁面繪制生成圖片领铐,然后保存到手機(jī)相冊,最終效果如下:

這里寫圖片描述

在這里我只寫頁面里關(guān)于 canvas 生成圖片并進(jìn)行保存這個(gè)流程的相關(guān)代碼宋舷,并且會(huì)在我踩過的坑那里進(jìn)行具體的講述绪撵。廢話不多說直接上干貨


wxml

<!-- 調(diào)用canvas圖片繪制方法 按鈕 -->
<view class="share" bindtap="share">
   <image class="share-img" src="/images/icon-share.png" mode="widthFix" lazy-load="true"></image>
   分享助力
</view>
<!-- 黑色透明層 -->
<view hidden="{{hiddenImg}}" class="mask"></view>
<!-- 調(diào)用保存圖片到手機(jī)相冊方法 按鈕 -->
<view class="share-btn" hidden="{{hiddenImg}}" bindtap="save">保存到相冊</view>
<!-- canvas -->
<view class='canvas-box'>
   <canvas canvas-id='share' style='width:100vw;height:100vh;' hidden='{{canvasHidden}}'></canvas>
</view>
<!-- canvas 繪制完成后顯示的圖片 -->
<image class="shareimg" src="{{shareImgPath}}" style="height:{{winHeight}}" mode="widthFix" hidden="{{hiddenImg}}"></image>

wxss

.share {
   width: 356rpx;
   height: 80rpx;
   text-align: center;
   line-height: 80rpx;
   color: #e73322;
   font-size: 32rpx;
   border-radius: 37rpx;
   background: #fff;
   margin: 70rpx auto 0;
}

.share-img {
   width: 31rpx;
}

.canvas-box {
   position: fixed;
   top: 99999px;
   left: 0;
   width: 100%;
}

.shareimg {
   position: fixed;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
}

.mask {
   position: fixed;
   z-index: 1;
   background: rgba(0, 0, 0, .5);
   top: 0;
   left: 0;
   bottom: 0;
   right: 0;
}

.share-btn {
   position: fixed;
   width: 356rpx;
   bottom: 50rpx;
   left: 197rpx;
   height: 80rpx;
   text-align: center;
   line-height: 80rpx;
   color: #e73322;
   font-size: 32rpx;
   border-radius: 37rpx;
   background: #fff;
   z-index: 999;
}

js

1. 設(shè)置 data 相關(guān)默認(rèn)值

  • canvasHidden: true 設(shè)置使 canvas 隱藏,因?yàn)?canvas 是原生組件祝蝠,擁有最高層級莲兢,如果不隱藏汹来,會(huì)影響頁面正常使用
  • hiddenImg: true 設(shè)置使黑色遮罩、保存按鈕和顯示生成后的圖片隱藏
data: {
   canvasHidden: true, 
   wxappName: "來「 老字號文化影響力 」測試你的知識等級",
   hiddenImg: true
},

2. 在 onLoad 方法中準(zhǔn)備需要用到的參數(shù)

這里需要注意一點(diǎn)我踩過的坑改艇,

  • 由于小程序的canvas 不能使用網(wǎng)絡(luò)圖片,所以緩存中的頭像不能直接用坟岔,需要使用wx.downloadFile方法將頭像路徑存儲為臨時(shí)路徑谒兄,以供 canvas 使用,由于之前不知道這個(gè)社付,所有折騰半天承疲,畫出來的圖片就是沒有頭像
  • 這里獲取設(shè)備寬度、高度以及設(shè)備像素比也非常重要鸥咖,用于最后顯示圖片的大小以及canvas畫圖過程中的寬高顯示
onLoad: function (o) {
   var that = this;
   //讀取緩存燕鸽,獲取微信頭像和昵稱
   wx.getStorage({
      key: 'user',
      success: function (res) {
         var nickName = res.data.nickName,
            avatarUrl = res.data.avatarUrl;
         that.setData({
            nickName: nickName,
         })
         // 由于canvas不能使用網(wǎng)絡(luò)圖片,所以此處進(jìn)行頭像臨時(shí)路徑存儲
         wx.downloadFile({
            url: avatarUrl,
            success: (res) => {
               that.setData({
                  avatarUrl: res.tempFilePath,
               })
            },
         });
      }
   })
   //獲取用戶設(shè)備信息啼辣,屏幕寬度
   wx.getSystemInfo({
      success: res => {
         that.setData({
            screenWidth: res.screenWidth,
            winHeight: res.windowHeight,
            ratio: res.pixelRatio
         })
      }
   })

},

3. 編寫 canvas 繪制方法

  • 第一步顯示畫板啊研,配置需要顯示的元素

這里需要注意 unit = that.data.screenWidth / 375 用于使用像素值進(jìn)行繪制后進(jìn)行手機(jī)不同機(jī)型大小的適配,context = wx.createCanvasContext('share') 用于指定要繪制的 canvas鸥拧,其余的參數(shù)都是我項(xiàng)目中需要用到的党远,各位童鞋可以根據(jù)自己的需求進(jìn)行配置

//定義的保存圖片方法
share: function () {
   wx.showLoading({
      title: '圖片生成中...',
   })
   var that = this;
   //設(shè)置畫板顯示,才能開始繪圖
   that.setData({
      canvasHidden: false
   })
   var res = that.data.res.result;
   var resImg;
   switch (res) {
      case 1:
         resImg = '/images/sdj.png';
         break;
      case 2:
         resImg = '/images/js.png';
         break;
      case 3:
         resImg = '/images/jr.png';
         break;
      case 4:
         resImg = '/images/gs.png';
         break;
      case 5:
         resImg = '/images/xc.png';
         break;
   }
   var unit = that.data.screenWidth / 375;
   var ratio = that.data.ratio;
   var screenWidth = that.data.screenWidth;
   var winHeight = that.data.winHeight;
   var bg = "/images/bg.png"
   var avatarUrl = that.data.avatarUrl;
   var bgleavel = "/images/bg-leavel.png";
   var qrcode = "/images/qrcode.jpg";
   var nickName = that.data.nickName;
   var context = wx.createCanvasContext('share');
   var idnum = that.data.res.id;
   var num = idnum.toString();
   var length = num.length;
   var left;
   switch (length) {
      case 2:
         left = 375 - 208 - length * 26
         break;

      case 3:
         left = 375 - 208 - length * 24
         break;

      case 4:
         left = 375 - 208 - length * 20
         break;

      case 5:
         left = 375 - 208 - length * 19
         break;

      default:
         left = 375 - 208 - length * 18
         break;
   }
   var wxappName = that.data.wxappName;
  • 第二步開始繪制將所需元素逐一繪制到畫板上

這里我有踩到的坑富弦,就是圖片繪制完成后沟娱,在手機(jī)上顯示非常模糊,多番查找折騰后腕柜,發(fā)現(xiàn)以下幾個(gè)地方設(shè)置好之后就OK了
width: screenWidth 設(shè)定指定的畫布區(qū)域的寬度
height: winHeight 設(shè)定指定的畫布區(qū)域的高度
destWidth: ratio * screenWidth 設(shè)定輸出圖片寬度
destHeight: ratio * winHeight 設(shè)定輸出圖片高度
quality: 1, 設(shè)定圖片質(zhì)量

destWidth 和 destHeight 需要設(shè)置為width 和height 的 2倍或以上才能讓圖片清晰济似,而現(xiàn)在的智能手機(jī)設(shè)備像素比一般都在2以上,所以這里直接用 ratio 來進(jìn)行設(shè)置

圖片繪制完成且臨時(shí)路徑生成之后盏缤,打開隱藏的遮罩層和保存按鈕以及供用戶瀏覽的生成之后的圖片

   // 繪制紅色背景
   context.drawImage(bg, 0, 0, that.data.screenWidth, winHeight)
   // 繪制頭像
   var avatarurl_width = unit * 75; //繪制的頭像寬度
   var avatarurl_heigth = unit * 75; //繪制的頭像高度
   var avatarurl_x = unit * 150; //繪制的頭像在畫布上的位置
   var avatarurl_y = unit * 35; //繪制的頭像在畫布上的位置

   context.save();
   //先畫個(gè)圓   前兩個(gè)參數(shù)確定了圓心 (x,y) 坐標(biāo)  第三個(gè)參數(shù)是圓的半徑  四參數(shù)是繪圖方向  默認(rèn)是false砰蠢,即順時(shí)針
   context.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI * 2, false);

   context.clip(); //畫好了圓 剪切  原始畫布中剪切任意形狀和尺寸。一旦剪切了某個(gè)區(qū)域蛾找,則所有之后的繪圖都會(huì)被限制在被剪切的區(qū)域內(nèi) 這也是我們要save上下文的原因

   context.drawImage(avatarUrl, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); // 將頭像放到繪制好的圓中

   context.restore(); //恢復(fù)之前保存的繪圖上下文狀態(tài) 還可以繼續(xù)繪制.

   // 繪制昵稱
   context.setFontSize(14)
   context.setFillStyle('#fff')
   context.setTextAlign('center')
   context.fillText(nickName, unit * 187, unit * 135)
   // 繪制知識等級背景圖
   context.drawImage(bgleavel, unit * 110, unit * 165, unit * 312 / 2, unit * 307 / 2)
   context.drawImage(resImg, unit * 160, unit * 180, unit * 110 / 2, unit * 238 / 2)
   // 繪制第幾位宣傳者
   context.setFontSize(16)
   context.setFillStyle('#fff')
   context.setTextAlign('right')
   context.fillText('你是第', left * unit, unit * 382)
   context.setFontSize(24)
   context.setFillStyle('#fff')
   context.setTextAlign('center')
   context.fillText(num, unit * (left + length * 12), unit * 382)
   context.setFontSize(16)
   context.setFillStyle('#fff')
   context.setTextAlign('left')
   context.fillText('位老字號文化傳播大使', unit * (left + length * 24), unit * 382)

   // 繪制二維碼
   context.drawImage(qrcode, unit * 138, unit * 410, unit * 204 / 2, unit * 204 / 2)
   // 繪制二維碼下部文字
   context.setFontSize(12)
   context.setFillStyle("#fff")
   context.setTextAlign("center")
   context.fillText("長按識別小程序", unit * 187.5, unit * 540)
   context.fillText(wxappName, unit * 187.5, unit * 560)
   //把畫板內(nèi)容繪制成圖片娩脾,并回調(diào) 畫板圖片路徑
   context.draw(false, function () {
      wx.canvasToTempFilePath({
         x: 0,
         y: 0,
         width: screenWidth,
         height: winHeight,
         destWidth: ratio * screenWidth,
         destHeight: ratio * winHeight,
         canvasId: 'share',
         quality: 1,
         success: function (res) {
            that.setData({
               shareImgPath: res.tempFilePath,
               hiddenImg: false,
               pathRes: res
            })
            if (!res.tempFilePath) {
               wx.showModal({
                  title: '提示',
                  content: '圖片繪制中,請稍后重試',
                  showCancel: false
               })
            }
            wx.hideLoading()
         }
      })
   });
},

4. 編寫保存到手機(jī)相冊方法

這里沒什么好說的打毛,就是調(diào)用微信提供的API 進(jìn)行圖片保存柿赊,成功或者失敗之后,進(jìn)行相應(yīng)提示并將一開始在data中設(shè)置的需要默認(rèn)隱藏的元素進(jìn)行隱藏幻枉,然后就OK了碰声,打開手機(jī)相冊就可以看到完成的圖片進(jìn)行朋友圈分享了。

save: function () {
   var that = this;
   var res = that.data.pathRes;
   wx.saveImageToPhotosAlbum({
      filePath: res.tempFilePath,
      //保存成功失敗之后熬甫,都要隱藏畫板胰挑,否則影響界面顯示。
      success: (res) => {
         wx.showToast({
            title: '保存成功',
            icon: 'none',
            duration: 1500,
            mask: false,
            success: function () {
               that.setData({
                  canvasHidden: true,
                  hiddenImg: true
               })
            }
         });
      },
      fail: (err) => {
         wx.showToast({
            title: '保存失敗',
            icon: 'none',
            duration: 1500,
            mask: false,
            success: function () {
               that.setData({
                  canvasHidden: true,
                  hiddenImg: true
               })
            }
         });
      }
   })
}

最后寫一下用到的API吧,常用的就不寫了主要寫一下我自己平時(shí)不怎么經(jīng)常用到的

  1. wx.downloadFile 用于將網(wǎng)絡(luò)圖片生成臨時(shí)路徑瞻颂,這里也有一個(gè)坑豺谈,需要在小程序公眾平臺將騰訊的 wx.qlogo.cn 這個(gè)域名設(shè)置為合法域名,否則會(huì)報(bào)錯(cuò)贡这,在之后的繪制中圖片盡量用本地路徑
  2. wx.createCanvasContext 其中在畫布中進(jìn)行繪制的時(shí)候如果有什么不明白的茬末,可以到 w3cschool 看看,鏈接是直接跳轉(zhuǎn)到 canvas 繪圖這一節(jié)的
  3. wx.canvasToTempFilePath 將畫板內(nèi)容繪制成圖片的方法盖矫,需要注意上面提到的影響手機(jī)圖片清晰度的參數(shù)丽惭,其中所有參數(shù)具體配置以及含義,可以直接到 官方文檔 查看
  4. wx.saveImageToPhotosAlbum 保存圖片到本地的API辈双,這個(gè)沒有難點(diǎn)责掏,不多說了

整篇文章看著代碼多,其實(shí)用到的不常見的API也就這幾個(gè)湃望,注意一下文章中我踩過的那幾個(gè)坑换衬,相信你可以開發(fā)出一個(gè)完美的帶小程序碼的用于分享到朋友圈的圖片了。

========================================================================================================================

2020年4月16日 補(bǔ)充

1喜爷、字體加粗
通過文字多次繪制可以模擬字體的加粗
2冗疮、標(biāo)題文字過長加省略號
通過 measureText 方法獲取標(biāo)題長度,與自己實(shí)際展示標(biāo)題長度做判斷檩帐,循環(huán)進(jìn)行截取术幔,直到滿足條件為止

var titleWidth = context.measureText(title).width;
context.fontSize = 16 * unit;
if(titleWidth > (320*unit)){
    while (titleWidth > (320*unit)) {
        title = title.substring(0, (title.length - 1));
        titleWidth = context.measureText(title).width;
        // console.log(title,titleWidth,i,title.length,(title.length - 1))
    }
    title = title + '...';
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者湃密。
  • 序言:七十年代末诅挑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泛源,更是在濱河造成了極大的恐慌拔妥,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件达箍,死亡現(xiàn)場離奇詭異没龙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)缎玫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門硬纤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赃磨,你說我怎么就攤上這事筝家。” “怎么了邻辉?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵溪王,是天一觀的道長腮鞍。 經(jīng)常有香客問我,道長莹菱,這世上最難降的妖魔是什么移国? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮道伟,結(jié)果婚禮上桥狡,老公的妹妹穿的比我還像新娘。我一直安慰自己皱卓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布部逮。 她就那樣靜靜地躺著娜汁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兄朋。 梳的紋絲不亂的頭發(fā)上掐禁,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機(jī)與錄音颅和,去河邊找鬼傅事。 笑死,一個(gè)胖子當(dāng)著我的面吹牛峡扩,可吹牛的內(nèi)容都是我干的蹭越。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼教届,長吁一口氣:“原來是場噩夢啊……” “哼响鹃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起案训,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤买置,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后强霎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忿项,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年城舞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轩触。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡椿争,死狀恐怖怕膛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秦踪,我是刑警寧澤褐捻,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布掸茅,位于F島的核電站,受9級特大地震影響柠逞,放射性物質(zhì)發(fā)生泄漏昧狮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一板壮、第九天 我趴在偏房一處隱蔽的房頂上張望逗鸣。 院中可真熱鬧,春花似錦绰精、人聲如沸撒璧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卿樱。三九已至,卻和暖如春硫椰,著一層夾襖步出監(jiān)牢的瞬間繁调,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工靶草, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹄胰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓奕翔,卻偏偏與公主長得像裕寨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子糠悯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容