利用canvas生成海報

JRucker發表於2019-02-26

準備

準備兩張圖片連線,最好是自己開發賬號驗證的https圖片連結。

實現思路

其實就是canvas實現方式,首先要就是定義一個canvas容器,把容器放在中間,圖片也要動態計算大小居中,顯示下面的文字和二維碼也是要根據容器動態去改變。

實現程式碼

  • 下載頭像,將圖片快取到本地
async onLoad() {
  let url1 = 'https://static-client-image.jrucker.cn/image/1547303314501.jpeg';
  let url2 = 'https://static-client-image.jrucker.cn/image/1550757251583.jpeg';

  this.getAsyncPic(url1, url2);
},
async getAsyncPic(url1, url2) {
  let res = await this.getUrlPromise(url1, url2);

  if (res.length && res.length === 2) {
    let [bannerObj, wxcodePicObj] = res;

    this.setData({
      'canvas.banner': bannerObj.banner,
      'canvas.wxcodePic': wxcodePicObj.wxcodePic
    });
  }
},
async getUrlPromise(url1, url2) {
  // 生成海報的封面圖快取到本地
  const promise1 = new Promise((resolve, reject) => {
    wx.getImageInfo({   //  小程式獲取圖片資訊API
      src: url1,
      success: function (res) {
        resolve({
          banner: res.path
        })
      },
      fail(err) {
        reject(err)
      }
    })
  });

  // 生成海報的小程式圖快取到本地
  const promise2 = new Promise((resolve, reject) => {
    wx.getImageInfo({   //  小程式獲取圖片資訊API
      src: url2,
      success: function (res) {
        resolve({
          wxcodePic: res.path
        })
      },
      fail(err) {
        reject(err)
      }
    })
  });

  let res = await Promise.all([promise1, promise2]);

  const promise = new Promise((resolve) => {
    resolve(res ? res : [])
  });

  return promise;
},
複製程式碼
  • 繪製海報
async setPoster() {
  if (this.data.posterImage) {
    return this.setData({
      visible: true,
      posterImage: this.data.posterImage
    })
  }

  wx.showLoading({title: '海報生成中...'});

  if (!this.data.canvas.banner || !this.data.canvas.wxcodePic) {
    let url1 = 'https://static-client-image.jrucker.cn/image/1547303314501.jpeg';
    let url2 = 'https://static-client-image.jrucker.cn/image/1550757251583.jpeg';

    await this.getAsyncPic(url1, url2);
  }

  const ctx = wx.createCanvasContext('canvas-box');
  ctx.setFillStyle('#ffffff');
  ctx.fillRect(0, 0, 270, 330);

  // 繪製頂部banner
  ctx.drawImage(this.data.canvas.banner, 0, 0, 270, 170);

  this.dealWords({
    ctx: ctx, // 畫布上下文
    fontSize: 16, // 字型大小
    word: this.data.canvas.title, // 需要處理的文字
    maxWidth: 255, // 一行文字最大寬度
    fillStyle: '#333333',
    x: 8, // 文字在x軸要顯示的位置
    y: 177, // 文字在y軸要顯示的位置
    maxLine: 1 // 文字最多顯示的行數
  });

  this.dealWords({
    ctx: ctx,
    fontSize: 14,
    word: this.data.canvas.desc,
    maxWidth: 260,
    fillStyle: '#999999',
    x: 8,
    y: 201,
    maxLine: 2
  });

  ctx.moveTo(0, 252);
  ctx.lineTo(270, 252);
  ctx.setLineWidth(0.5);
  ctx.setStrokeStyle('#f2f2f2');
  ctx.stroke();

  // 繪製小程式碼
  ctx.drawImage(this.data.canvas.wxcodePic, 9, 261, 60, 60);
  ctx.setFontSize(13);
  ctx.setFillStyle('#999999');
  ctx.fillText('長按掃碼檢視詳情', 80, 296);
  ctx.draw();

  setTimeout(() => {
    wx.canvasToTempFilePath({
      quality: 1,
      canvasId: 'canvas-box',
      success: (res) => {
        let tempFilePath = res.tempFilePath;
        wx.hideLoading();
        this.setData({
          visible: true,
          posterImage: tempFilePath
        })
      },
      fail: (res) => {
        console.log(res);
      }
    });
  }, 1000)
}
複製程式碼
  • 繪製多行文字
dealWords(options) {
  options.ctx.setFontSize(options.fontSize);//設定字型大小
  let allRow = Math.ceil(options.ctx.measureText(options.word).width / options.maxWidth);//實際總共能分多少行
  let count = allRow >= options.maxLine ? options.maxLine : allRow;//實際能分多少行與設定的最大顯示行數比,誰小就用誰做迴圈次數
  let endPos = 0;//當前字串的截斷點
  for (let j = 0; j < count; j++) {
    let nowStr = options.word.slice(endPos);//當前剩餘的字串
    let rowWid = 0;//每一行當前寬度
    options.ctx.setFillStyle(options.fillStyle);

    if (options.ctx.measureText(nowStr).width > options.maxWidth) {//如果當前的字串寬度大於最大寬度,然後開始擷取
      for (let m = 0; m < nowStr.length; m++) {
          rowWid += options.ctx.measureText(nowStr[m]).width;//當前字串總寬度
        if (rowWid > options.maxWidth) {
          if (j === options.maxLine - 1) { //如果是最後一行
            options.ctx.fillText(nowStr.slice(0, m - 1) + '...', options.x, options.y + (j + 1) * 18);  //(j+1)*18這是每一行的高度
          } else {
            options.ctx.fillText(nowStr.slice(0, m), options.x, options.y + (j + 1) * 18);
          }
          endPos += m;//下次截斷點
          break;
        }
      }
    } else {//如果當前的字串寬度小於最大寬度就直接輸出
      options.ctx.fillText(nowStr.slice(0), options.x, options.y + (j + 1) * 18);
    }
  }
}
複製程式碼
  • 儲存繪製圖片至相簿,需提前判斷是否已授權,這一步就是最後把canvas生成圖片了,大功告成。
savePoster() {
  const canvasToTempFilePath = () => {
    wx.showLoading({title: '載入中'});

    wx.canvasToTempFilePath({
      quality: 1,
      canvasId: 'canvas-box',
      fileType: 'png',
      success: (res) => {
        wx.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success: (res) => {
            wx.hideLoading();
            wx.showToast({
              title: '儲存成功',
            });
            this.setData({
              visible: false
            })
          },
          fail() {
            wx.hideLoading()
          }
        })
      }
    })
  };
  /*檢視是否授權*/
  wx.getSetting({
    success: (res) => {
      if (!res.authSetting['scope.writePhotosAlbum']) {
        wx.authorize({
          scope: 'scope.writePhotosAlbum',
          success() { //這裡是使用者同意授權後的回撥
            canvasToTempFilePath();
          },
          fail() { //這裡是使用者拒絕授權後的回撥
            wx.showModal({
              title: '提示',
              content: '若不開啟授權,則無法將圖片儲存在相簿中!',
              showCancel: true,
              cancelText: '暫不授權',
              cancelColor: '#000000',
              confirmText: '去授權',
              confirmColor: '#3CC51F',
              success: function (res) {
                if (res.confirm) {
                  wx.openSetting({
                    //調起客戶端小程式設定介面,返回使用者設定的操作結果。
                  })
                } else {
                  console.log('使用者點選取消')
                }
              }
            })
          }
        })
      } else {
        canvasToTempFilePath()
      }
    }
  });
}
複製程式碼

注意

1、圖片要提前下載

這裡面還有一個問題就是,圖片要提前下載完之後再繪圖,不然圖片顯示不出來,可以把下載圖片的方法單獨拎出來,然後下載圖片後回撥繪圖方法。

2、ctx.draw()

這個方法是在繪製完成之後在呼叫,不然容易其它被覆蓋。

效果圖

海報

相關文章