1. 程式人生 > >微信小程式採坑記錄 ------- canvas 生成分享到朋友圈帶小程式碼的圖片

微信小程式採坑記錄 ------- canvas 生成分享到朋友圈帶小程式碼的圖片

最近做了一個問卷類的小程式,其中的結果頁想讓使用者進行朋友圈分享轉發,網上搜索資料,得出解決思路,用 canvas
將頁面繪製生成圖片,然後儲存到手機相簿,最終效果如下:

這裡寫圖片描述

在這裡我只寫頁面裡關於 canvas 生成圖片並進行儲存這個流程的相關程式碼,並且會在我踩過的坑那裡進行具體的講述。廢話不多說直接上乾貨


wxml

<!-- 呼叫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>
<!-- 呼叫儲存圖片到手機相簿方法 按鈕 -->
<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. 設定 data 相關預設值

  • canvasHidden: true 設定使 canvas 隱藏,因為 canvas 是原生元件,擁有最高層級,如果不隱藏,會影響頁面正常使用
  • hiddenImg: true 設定使黑色遮罩、儲存按鈕和顯示生成後的圖片隱藏
data: {
   canvasHidden: true, 
   wxappName: "來「 老字號文化影響力 」測試你的知識等級",
   hiddenImg: true
},

####2. 在 onLoad 方法中準備需要用到的引數
這裡需要注意一點我踩過的坑,

  • 由於小程式的canvas 不能使用網路圖片,所以快取中的頭像不能直接用,需要使用wx.downloadFile方法將頭像路徑儲存為臨時路徑,以供 canvas 使用,由於之前不知道這個,所有折騰半天,畫出來的圖片就是沒有頭像
  • 這裡獲取裝置寬度、高度以及裝置畫素比也非常重要,用於最後顯示圖片的大小以及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不能使用網路圖片,所以此處進行頭像臨時路徑儲存
         wx.downloadFile({
            url: avatarUrl,
            success: (res) => {
               that.setData({
                  avatarUrl: res.tempFilePath,
               })
            },
         });
      }
   })
   //獲取使用者裝置資訊,螢幕寬度
   wx.getSystemInfo({
      success: res => {
         that.setData({
            screenWidth: res.screenWidth,
            winHeight: res.windowHeight,
            ratio: res.pixelRatio
         })
      }
   })

},

####3. 編寫 canvas 繪製方法

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

這裡需要注意 unit = that.data.screenWidth / 375 用於使用畫素值進行繪製後進行手機不同機型大小的適配,context = wx.createCanvasContext('share') 用於指定要繪製的 canvas,其餘的引數都是我專案中需要用到的,各位童鞋可以根據自己的需求進行配置

//定義的儲存圖片方法
share: function () {
   wx.showLoading({
      title: '圖片生成中...',
   })
   var that = this;
   //設定畫板顯示,才能開始繪圖
   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;
  • 第二步開始繪製將所需元素逐一繪製到畫板上

這裡我有踩到的坑,就是圖片繪製完成後,在手機上顯示非常模糊,多番查詢折騰後,發現以下幾個地方設定好之後就OK了
width: screenWidth 設定指定的畫布區域的寬度
height: winHeight 設定指定的畫布區域的高度
destWidth: ratio * screenWidth 設定輸出圖片寬度
destHeight: ratio * winHeight 設定輸出圖片高度
quality: 1, 設定圖片質量

destWidth 和 destHeight 需要設定為width 和height 的 2倍或以上才能讓圖片清晰,而現在的智慧手機裝置畫素比一般都在2以上,所以這裡直接用 ratio 來進行設定

圖片繪製完成且臨時路徑生成之後,開啟隱藏的遮罩層和儲存按鈕以及供使用者瀏覽的生成之後的圖片

   // 繪製紅色背景
   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();
   //先畫個圓   前兩個引數確定了圓心 (x,y) 座標  第三個引數是圓的半徑  四引數是繪圖方向  預設是false,即順時針
   context.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI * 2, false);

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

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

   context.restore(); //恢復之前儲存的繪圖上下文狀態 還可以繼續繪製.

   // 繪製暱稱
   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)
   //把畫板內容繪製成圖片,並回調 畫板圖片路徑
   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. 編寫儲存到手機相簿方法
這裡沒什麼好說的,就是呼叫微信提供的API 進行圖片儲存,成功或者失敗之後,進行相應提示並將一開始在data中設定的需要預設隱藏的元素進行隱藏,然後就OK了,開啟手機相簿就可以看到完成的圖片進行朋友圈分享了。

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吧,常用的就不寫了主要寫一下我自己平時不怎麼經常用到的

  1. wx.downloadFile 用於將網路圖片生成臨時路徑,這裡也有一個坑,需要在小程式公眾平臺將騰訊的 wx.qlogo.cn 這個域名設定為合法域名,否則會報錯,在之後的繪製中圖片儘量用本地路徑
  2. wx.createCanvasContext 其中在畫布中進行繪製的時候如果有什麼不明白的,可以到 w3cschool 看看,連結是直接跳轉到 canvas 繪圖這一節的
  3. wx.canvasToTempFilePath 將畫板內容繪製成圖片的方法,需要注意上面提到的影響手機圖片清晰度的引數,其中所有引數具體配置以及含義,可以直接到 官方文件 檢視
  4. wx.saveImageToPhotosAlbum 儲存圖片到本地的API,這個沒有難點,不多說了

整篇文章看著程式碼多,其實用到的不常見的API也就這幾個,注意一下文章中我踩過的那幾個坑,相信你可以開發出一個完美的帶小程式碼的用於分享到朋友圈的圖片了。