微信小程序使用canvas實現生成海報圖的功能

最近開發中要實現一個需求,用戶點擊分享生成一個圖片海報,能夠供用戶發朋友圈或者其餘使用(感受沒人會用~),用到了小程序canvas,和web上的功能基本同樣,記錄一下。git

---還沒上線一週,這個功能就被撤了github

首先預設幾個工具函數web

  • 1/小程序開發使用的是 rpx,可是canvas裏的單位使用的 px ,就須要轉換設計圖的尺寸
// 單位轉換
const rpx2px = rpx => deviceWidth / 750 * rpx

其中deviceWidth就是設備的寬度,能夠經過wx.getSystemInfo()獲取到,即windowWidthcanvas

  • 二、獲取網絡圖片的信息
// 獲取圖片信息
const getImageInfo = url => {
  return new Promise((resolve, reject) => {
    wx.getImageInfo({
      src: url,
      success: resolve,
      fail: reject,
    })
  })
}

須要注意的是須要添加downloadFile的合法域名小程序

  • 三、文本換行
/**
 * 設置文本行數,超出省略
 * @param {object} ctx canvas實例
 * @param {string} text 文本數據
 * @param {number} lineNum 行數
 * @param {number} width 文字寬度最寬默認 620rpx
 * 返回 由每一行組成的數組
 */
const setTextLine = (ctx, text, lineNum = 1, width = 610) => {
  const str_arr = String(text).split('')
  width = rpx2px(width)
  let temp = ''
  // 分行
  let row_arr = str_arr.reduce((arr, word) => {
    const w = ctx.measureText(temp).width
    if (w < width) {
      temp += word;
    } else {
      arr.push(temp)
      temp = word
    }
    return arr
  }, [])
  row_arr.push(temp)
  temp = ''

  // 判斷須要的行數
  row_arr = row_arr.slice(0,lineNum)
  if (row_arr.length > 1){
    // 最後一行超出則省略號
    row_arr[row_arr.length - 1].split().every(v => {
      temp += v
      if (ctx.measureText(temp).width > (width - 20)) {
        temp += '...'
        return false
      }
      return true
    })
    // row_arr.pop()
    // row_arr.push(temp)
    row_arr.splice(row_arr.length - 1, 1, temp)
  }

  return row_arr
}

這裏就是藉助ctx.measureText()來獲取文本的寬度,看是否會超出canvas的寬度,而後根據傳參來控制行數,原理就是給最後一行的內容添加省略號便可,函數裏我沒有判斷僅需一行可是超出一行的狀況,由於咱們是容許2行的,因此不須要判斷,有須要能夠自行添加數組

接下來就能夠執行繪製過程了,首先若是咱們有用到網絡圖片。則須要在圖片下載到本地後開始,promise

...
const url1 = getImageInfo(url1)
const url2 = getImageInfo(url2)
Promise.all([product_img]).then(([url1, url2]) => {
...
})
...

這裏就是用到了promise,不熟的能夠看這裏promise網絡

Promise.all中獲取canvasapp

...
        const ctx = wx.createCanvasContext('myCanvas', this)
        // 繪製背景
        const canvas_W = rpx2px(705)
        const canvas_H = rpx2px(1180)
        ctx.setFillStyle('#fff')
        ctx.fillRect(0, 0, canvas_W, canvas_H)
		...

繪製背景的目的是防止生成的圖片無內容區域透明,固然有特殊需求的能夠不 接下來的繪製圖片的話,使用的ctx.drawImage()這個API,傳入圖片的左上角座標和寬高便可,示例:函數

// 繪製logo
        const logo_SX = rpx2px(35)
        const logo_SY = rpx2px(23)
        const logo_W = rpx2px(253)
        const logo_H = rpx2px(80)
        ctx.drawImage('/images/logo.png', logo_SX, logo_SY, logo_W, logo_H)

繪製文本時,若是文本長度不肯定,使用上述封裝的函數處理文本內容,超出添加省略號便可,使用ctx.fillText()繪製

所有繪製完成以後,調用ctx.draw(cb)將以前在繪圖上下文中的描述(路徑、變形、樣式)畫到 canvas 中。 若是不操做生成圖片,到這一步就結束了,須要生成圖片操做的,在回調函數中進行操做便可

ctx.draw(false, () => {
          wx.canvasToTempFilePath({
            canvasId: 'myCanvas',
            quality: 1,
            success: result => {
              ...something
            }
          }, this)
        })

把當前畫布指定區域的內容導出生成指定大小的圖片。在 draw() 回調裏調用該方法才能保證圖片導出成功. 這裏的quality是對jpg格式圖片的壓縮範圍,0-1之間的值,web上使用canvas,在toDataURL的第二個參數,能夠實現壓縮圖片(jpg/webp)

圖片保存到手機:

wx.saveImageToPhotosAlbum({
            filePath: result.tempFilePath,
            success: res1 => {
              app.showTips(0, '圖片已保存')
            },
            fail: err => {
              app.showTips(0, '圖片生成失敗')
            }
          })

到這裏圖片保存完成,可是實際使用中,該功能須要使用用戶的相冊權限,若是用戶拒絕了受權,在下次調用的時候,就會直接失敗 因此須要給用戶一個友好的提示,即便用wx.getSetting獲取用戶受權狀況,若是用戶拒絕過受權,則提示用戶進入設置頁面手動進行受權

另一個問題是圖片的生成質量,若是圖片生成以後不夠高清,能夠考慮將像素倍數提高

完整代碼已上傳GitHub能夠測試:https://github.com/dudumifan/WeChat-Mini-Program-Generate-poster

相關文章
相關標籤/搜索