微信小程序canvas繪制

近日接觸到微信小程序canvas繪制海報(bào)移稳,經(jīng)過(guò)一番折騰犀勒,算是能保存了屎飘,記錄一下吧。

新版本Api接口

 <!-- canvas.wxml -->
<!-- width,height是根據(jù)設(shè)計(jì)圖及rpx與px像素比計(jì)算出來(lái)的寬高-->
 <!-- wx.getSystemInfo({
      success: (res) => {
        if(res.model.indexOf("iPhone X") > -1){
          this.globalData.distance = 36
        }
        this.globalData.rpx = res.windowWidth / 750
        this.globalData.width = 635 * (res.windowWidth / 750)
        this.globalData.height = 974 * (res.windowWidth / 750)
      },
    })-->
<canvas type="2d" id="myCan" style="width: {{width}}px;height:{{height}}px"></canvas>

生成海報(bào)

 saveImg(res) {
    const index = res.detail
    wx.showLoading({
      title: '海報(bào)生成中'
    })
    // 新接口 canvas2d
    query.select('#myCan')
      .fields({
        node: true,
        size: true
      })
      .exec((res) => {
        console.log(res[0].node)
        const canvas = res[0].node
        const ctx = canvas.getContext('2d')
        const dpr = wx.getSystemInfoSync().pixelRatio
        canvas.width = res[0].width * dpr
        canvas.height = res[0].height * dpr
        ctx.scale(dpr, dpr)
        saveCanvers(canvas, ctx, this.data.images[index], this.data.postersInfo.title, this.data.postersInfo.desc, this.data.postersInfo.desc1, this.data.avatar, '入秋的番薯', this.data.miniCode)
      })

  },

方法封裝

async function saveCanvers(canvas, ctx, headImg, title, desc, desc1, codeImg) {
  const App = getApp()
  let rpx = App.globalData.rpx
  // 海報(bào)解釋下面向上移動(dòng)的距離 如果兩行解釋  那么不上移  如果一行解釋  那么上移35
  let upMove = desc1 ? 0 : 35
  // 背景色
  ctx.fillStyle = '#ffffff'
  // canvas大小
  ctx.fillRect(0, 0, 638 * rpx, (974 - upMove) * rpx)
  // 繪制主圖
  await downLoadDrow(canvas, ctx, headImg, 0, 0, 638 * rpx, 558 * rpx)
  // 標(biāo)題
  ctx.font = 36 * rpx + 'px'
  ctx.fillStyle = '#373B3E'
  ctx.fillText(title, 30 * rpx, 608 * rpx, 589 * rpx)
  // 解釋
  ctx.font = 24 * rpx + 'px'
  ctx.fillStyle = '#9D9D9D'
  ctx.fillText(desc, 30 * rpx, 648 * rpx, 589 * rpx)
  desc1 && ctx.fillText(desc1, 30 * rpx, 684 * rpx, 589 * rpx)
  // 虛線
  ctx.strokeStyle = '#DEDEDE'
  ctx.setLineDash([3, 3]);
  ctx.beginPath();
  ctx.moveTo(30 * rpx, (708 - upMove) * rpx);
  ctx.lineTo(610 * rpx, (708 - upMove) * rpx);
  ctx.stroke();
  // 圓形頭像
  ctx.save()
  ctx.beginPath()
  ctx.arc(72 * rpx, (808 - upMove) * rpx, 42 * rpx, 0, 2 * Math.PI)
  // 保證原型填充無(wú)bug
  ctx.fill()
  ctx.clip()
  await downLoadDrow(canvas, ctx, userInfo.avatarUrl, 30 * rpx, (766 - upMove) * rpx, 84 * rpx, 84 * rpx)
  ctx.restore()
  // 用戶昵稱
  ctx.font = 32 * rpx + 'px'
  ctx.fillStyle = '#000000'
  ctx.fillText(userInfo.nickName, 134 * rpx, (813 - upMove) * rpx, 200 * rpx)
  // 邀請(qǐng)掃碼
  ctx.font = 24 * rpx + 'px'
  ctx.fillText('邀您掃碼體驗(yàn)', 30 * rpx, (888 - upMove) * rpx, 200 * rpx)
  // 房車小程序
  ctx.fillText('上汽大通原廠房車小程序', 30 * rpx, (930 - upMove) * rpx, 300 * rpx)
  // 小程序碼
  await downLoadDrow(canvas, ctx, codeImg, 400 * rpx, (738 - upMove) * rpx, 206 * rpx, 206 * rpx)
  setTimeout(() => {
    wx.canvasToTempFilePath({
      canvas: canvas,
      success: (can) => {
        wx.getSetting({
          success(res) {
            if (!res.authSetting['scope.writePhotosAlbum']) { //判斷權(quán)限
              wx.authorize({ //獲取權(quán)限
                scope: 'scope.writePhotosAlbum',
                success() {
                  saveImg(can.tempFilePath)
                }
              })
            } else {
              saveImg(can.tempFilePath)
            }
          }
        })
      },
      fail: (err) => {
        wx.showToast({
          title: '保存失敗贾费,請(qǐng)稍后重試',
          icon: 'none'
        })
        return false
      }
    })
  }, 500)
}
function downLoadDrow(canvas, ctx, url, x, y, w, h) {
  return new Promise((resolve, reject) => {
    wx.getImageInfo({
      src: url,
      success: (res) => {
        const img = canvas.createImage();
        img.src = res.path; //微信請(qǐng)求返回頭像
        img.onload = () => {
          console.log(img)
          ctx.drawImage(img, x, y, w, h);
          img.src = ""
          resolve()
        }
      },
      fail: (err) => {
        wx.hideLoading()
        wx.showToast({
          title: '下載圖片失敗钦购,請(qǐng)稍后重試',
          icon: 'none'
        })
        reject()
        return false
      }
    })
  })
}
function saveImg(path) {
  wx.hideLoading()
  wx.saveImageToPhotosAlbum({
    filePath: path,
    success: (res) => {
      wx.showToast({
        title: '已保存到相冊(cè)',
        icon: 'success',
        duration: 2000
      })
    },
    fail: (err) => {
      wx.showToast({
        title: '保存失敗',
        icon: 'none'
      });
    }
  })
}

老是在繪制的時(shí)候閃退,內(nèi)存溢出褂萧,也不知道怎么解決押桃,網(wǎng)上找了點(diǎn)方法,但好像都不行导犹,也可能沒(méi)找到重點(diǎn)唱凯,不得已羡忘,再來(lái)一套老版本canvas

老版本canvas

<canvas canvas-id="myCan" style="width: {{width}}px;height:{{height}}px"></canvas>

生成圖片

 saveImg(res) {
    // 組件里面?zhèn)鬟f的圖片索引  不在細(xì)說(shuō) 可忽略
    const index = res.detail
    wx.showLoading({
      title: '海報(bào)生成中'
    })
    let ctx = wx.createCanvasContext("myCan")
    saveCanversOld(ctx, this.data.postersInfo.swiper[index], this.data.postersInfo.title, this.data.postersInfo.desc, this.data.postersInfo.desc1, this.data.postersInfo.buffer)
  },

方法封裝

async function saveCanversOld(ctx, headImg, title, desc, desc1, codeImg) {
  let userInfo = wx.getStorageSync('userInfo')
  const App = getApp()
  let rpx = App.globalData.rpx
  let upMove = desc1 ? 0 : 35
  // 背景色
  ctx.setFillStyle('#ffffff')
  // canvas大小
  ctx.fillRect(0, 0, 638 * rpx, (974 - upMove) * rpx)
  // 繪制主圖
  await downLoadDrowOld(ctx, headImg, 0, 0, 638 * rpx, 558 * rpx, 'headImg')
  // 標(biāo)題
  ctx.setFontSize(36 * rpx)
  ctx.setFillStyle('#373B3E')
  ctx.fillText(title, 30 * rpx, 608 * rpx, 589 * rpx)
  // 解釋
  ctx.setFontSize(24 * rpx)
  ctx.setFillStyle('#9D9D9D')
  ctx.fillText(desc, 30 * rpx, 648 * rpx, 589 * rpx)
  desc1 && ctx.fillText(desc1, 30 * rpx, 684 * rpx, 589 * rpx)
  // 虛線
  ctx.setStrokeStyle('#DEDEDE')
  ctx.setLineDash([6, 5]);
  ctx.beginPath();
  ctx.moveTo(30 * rpx, (708 - upMove) * rpx);
  ctx.lineTo(610 * rpx, (708 - upMove) * rpx);
  ctx.stroke();
  // 圓形頭像
  ctx.save()
  ctx.beginPath()
  ctx.arc(72 * rpx, (808 - upMove) * rpx, 42 * rpx, 0, 2 * Math.PI)
  // 保證原型填充無(wú)bug
  ctx.fill()
  ctx.clip()
  await downLoadDrowOld(ctx, userInfo && userInfo.avatarUrl || config.defaultImg, 30 * rpx, (766 - upMove) * rpx, 84 * rpx, 84 * rpx)
  ctx.restore()
  // 用戶昵稱
  ctx.setFontSize(32 * rpx)
  ctx.setFillStyle('#000000')
  ctx.fillText(userInfo && userInfo.nickName || config.defaultUser, 134 * rpx, (813 - upMove) * rpx, 200 * rpx)
  // 邀請(qǐng)掃碼
  ctx.setFontSize(24 * rpx)
  ctx.fillText('邀您掃碼體驗(yàn)', 30 * rpx, (888 - upMove) * rpx, 200 * rpx)
  // 房車小程序
  ctx.fillText('上汽大通原廠房車小程序', 30 * rpx, (930 - upMove) * rpx, 300 * rpx)
  // 小程序碼
  await downLoadDrowOld(ctx, codeImg, 400 * rpx, (738 - upMove) * rpx, 206 * rpx, 206 * rpx, 'miniCode')
  ctx.draw(false, () => {
    wx.canvasToTempFilePath({
      fileType: 'jpg',
      canvasId: 'myCan',
      success: (res) => {
        console.log(res.tempFilePath)
        saveImg(res.tempFilePath)
      },
      fail: (err) => {
        console.log(err)
      }
    })
  })
}
// 下載圖片
function downLoadDrowOld(ctx, url, x, y, w, h, type = '') {
  return new Promise((resolve, reject) => {
    if (type == 'miniCode') {
      //聲明文件系統(tǒng)
      const fs = wx.getFileSystemManager();
      //隨機(jī)定義路徑名稱
      var times = new Date().getTime();
      var codeimg = wx.env.USER_DATA_PATH + '/' + times + '.png';
      //將base64圖片寫入
      fs.writeFile({
        filePath: codeimg,
        data: url,
        encoding: 'base64',
        success: (res) => {
          //寫入成功了的話,新的圖片路徑就能用了
          ctx.drawImage(codeimg, x, y, w, h)
          resolve()
          // 繪制成功之后要釋放 內(nèi)存
          setTimeout(() => {
            fs.unlink({
              filePath: codeimg,
              success: (res) => {
                console.log('清除文件成功', res)
              },
              err: (err) => {
                wx.hideLoading()
                wx.showToast({
                  title: '釋放文件內(nèi)存失敗',
                  icon: 'none'
                })
              }
            })
          }, 2000)
        }
      });
    } else if (type == 'headImg') {
      // 獲取圖片信息進(jìn)行截取 截取的起止位置也需要計(jì)算  然后繪制  x, y, w, h只代表繪制到canvas上的范圍
      wx.getImageInfo({
        src: url,
        success: (res) => {
          // 根據(jù)ui算出來(lái)的要展示的比例  width / height
          const rat = 1.143
          // 圖片的寬度
          const imgWidth = res.width
          // 圖片的高度
          const imgHeight = res.height
          // 剪切圖片的寬度磕昼, 高度卷雕, 剪切X起始位置, 剪切Y起始位置
          let cutWidth, cutHeight, cutStartX, cutStartY
          if (imgWidth <= imgHeight) {
            cutWidth = imgWidth
            cutHeight = imgWidth / rat
            cutStartX = 0
            cutStartY = (imgHeight - cutHeight) / 2
          } else {
            cutHeight = imgHeight
            cutWidth = imgHeight * rat
            cutStartX = (imgWidth - cutWidth) / 2
            cutStartY = 0
          }
          ctx.drawImage(res.path, cutStartX, cutStartY, cutWidth, cutHeight, x, y, w, h)
          resolve()
        },
        fail: (err) => {
          console.log(err)
          wx.hideLoading()
          wx.showToast({
            title: '獲取圖片信息失敗',
            icon: 'none'
          })
          reject()
          return false
        }
      })
    } else {
      wx.downloadFile({
        url,
        success: (res) => {
          ctx.drawImage(res.tempFilePath, x, y, w, h)
          resolve()
        },
        fail: (err) => {
          wx.hideLoading()
          wx.showToast({
            title: '下載圖片失敗票从,請(qǐng)稍后重試',
            icon: 'none'
          })
          reject()
          return false
        }
      })
    }
  })
}
function saveImg(path) {
  wx.hideLoading()
  wx.saveImageToPhotosAlbum({
    filePath: path,
    success: (res) => {
      wx.showToast({
        title: '已保存到相冊(cè)',
        icon: 'success',
        duration: 2000
      })
    },
    fail: (err) => {
      wx.showToast({
        title: '保存失敗',
        icon: 'none'
      });
    }
  })
}

里面有一些buffer文件的轉(zhuǎn)換漫雕,繪制圖片時(shí)比例大小的裁剪,分享圖的后臺(tái)配置愈合與獲取等峰鄙,都是比較常規(guī)的業(yè)務(wù)浸间,就不記了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吟榴,一起剝皮案震驚了整個(gè)濱河市魁蒜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌煤墙,老刑警劉巖梅惯,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異仿野,居然都是意外死亡铣减,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門脚作,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)葫哗,“玉大人,你說(shuō)我怎么就攤上這事球涛×诱耄” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵亿扁,是天一觀的道長(zhǎng)捺典。 經(jīng)常有香客問(wèn)我,道長(zhǎng)从祝,這世上最難降的妖魔是什么襟己? 我笑而不...
    開(kāi)封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮牍陌,結(jié)果婚禮上擎浴,老公的妹妹穿的比我還像新娘。我一直安慰自己毒涧,他們只是感情好贮预,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般仿吞。 火紅的嫁衣襯著肌膚如雪滑频。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天茫藏,我揣著相機(jī)與錄音误趴,去河邊找鬼。 笑死务傲,一個(gè)胖子當(dāng)著我的面吹牛凉当,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播售葡,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼看杭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了挟伙?” 一聲冷哼從身側(cè)響起楼雹,我...
    開(kāi)封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尖阔,沒(méi)想到半個(gè)月后贮缅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡介却,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年谴供,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片齿坷。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桂肌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出永淌,到底是詐尸還是另有隱情崎场,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布遂蛀,位于F島的核電站谭跨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏李滴。R本人自食惡果不足惜螃宙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悬嗓。 院中可真熱鬧污呼,春花似錦裕坊、人聲如沸包竹。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)周瞎。三九已至苗缩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間声诸,已是汗流浹背酱讶。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彼乌,地道東北人泻肯。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像慰照,于是被迫代替她去往敵國(guó)和親灶挟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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