小程序画布canvas

/**
需要绘制的内容有:
> 图片
  1、背景图
  2、头像
  3、邀请语
  4、商品图
  5、二维码
  6、指纹

>文字
  1、0
  2、元抢购
  3、原价¥999.00
  4、商品标题
  5、长按识别二维码 进入活动
  6、此二维码已安全认证,可以放心扫描*/

// 绘制圆形头像
function circleImg(ctx, img, x, y, r) {
  ctx.save();
  var d = 2 * r;
  var cx = x + r;
  var cy = y + r;
  ctx.arc(cx, cy, r, 0, 2 * Math.PI);
  ctx.clip();
  ctx.drawImage(img, x, y, d, d);
  ctx.restore();
}

// 文字换行
function textWrap(ctx, str, initX, initY, lineHeight, cavWithd) {
  let lineWidth = 0;
  let canvasWidth = cavWithd ? cavWithd : 240;
  let lastSubStrIndex = 0;
  if (!lineHeight) {
    lineHeight = 20;
  }
  let _addLineHeight = 0;
  for (let i = 0; i < str.length; i++) {
    lineWidth += ctx.measureText(str[i]).width;
    if (lineWidth > canvasWidth - initX) {  //减去initX,防止边界出现的问题
      ctx.fillText(str.substring(lastSubStrIndex, i), initX, initY);
      initY += lineHeight;
      lineWidth = 0;
      lastSubStrIndex = i;
      _addLineHeight += lineHeight;
      //++lineNum
    }
    if (i == str.length - 1) {
      ctx.fillText(str.substring(lastSubStrIndex, i + 1), initX, initY);
    }
  }
  return _addLineHeight;
}

let drawCanvas = function (that, shareInfo) {
  if(that.data.resultimg){
    return
  }
  let userInfoData = wx.getStorageSync('userInfoData') || {}
  // 图片
  let img = {
    bg: 'https://dahoutai.side.vip/project/activity/theme/poster/wx-share-bg2.jpg',
    avatar: shareInfo.avatar || 'https://dahoutai.side.vip/project/app/memberCard/default-avatar.png',
    text: 'https://dahoutai.side.vip/project/activity/theme/poster/text.png',
    goods: shareInfo.goods,
    logo: userInfoData.brandlogo,
    gou: 'https://dahoutai.side.vip/project/activity/theme/poster/gou.png',
    qrcode: shareInfo.qrcode,
    fingerprint: 'https://dahoutai.side.vip/project/activity/theme/poster/two-code.png'
  }

  // 文字内容
  let content = {
    zero: shareInfo.price,
    mTitle: '元抢购',
    oPrice: `原价¥${shareInfo.cprice}`,
    pTitle: shareInfo.prize,
    tip: '长按识别二维码 进入活动',
    scan: '此二维码已安全认证,可以放心扫描',
  }

  // 将图片地址转为临时图片
  let num = 0
  let goodsRate = 1 // 商品图宽高比例
  let logoRate = 1 // logo比例
  for (let i in img) {
    wx.getImageInfo({
      src: img[i],
      success: function (res) {
        if(i == 'goods'){
          goodsRate = res.height/res.width
        }
        if(i == 'logoRate'){
          logoRate = res.height/res.width
        }
        img[i] = res.path
        ++ num

        if(num == 8){ // 这里有个异步问题
          console.log('开始绘图')
          // 绘制canvas
          let rate = .5 // 缩小比例
          let ctx = wx.createCanvasContext('myCanvas',that)
          /**图片绘制*/
          ctx.drawImage(img.bg,0,0,750*rate,1334*rate) // 背景
          circleImg(ctx,img.avatar,30*rate,139*rate,33*rate) // 绘制头像
          ctx.drawImage(img.text,86*rate,139*rate,632*rate,68*rate) // 推荐语

          // 商品图需要做宽高自适应居中
          // 图片以高度为自适应
          // 如果自适应高度之后的图片,超出ui的最大宽度,则再次进行宽度自适应调整和高度自适应调整
          let goodsMaxWidth = 560*rate, // 商品ui最大宽度
            goodsMaxHeight = 372*rate, // 商品ui最大高度
            goodsWidth = 372/goodsRate*rate,
            goodsHeight = 372*rate,
            goodsDeviationX = 0,  // X轴偏移
            goodsDeviationY = 0   // Y轴偏移
          if( goodsWidth > goodsMaxWidth){
            goodsHeight = goodsHeight * (goodsMaxWidth/goodsWidth)
            goodsWidth = goodsMaxWidth
            goodsDeviationY = (goodsMaxHeight - goodsHeight)/2
          }else {
            goodsDeviationX = (goodsMaxWidth - goodsWidth)/2
          }

          ctx.drawImage(img.goods,95*rate+goodsDeviationX,436*rate+goodsDeviationY,goodsWidth,goodsHeight) // 商品图
          ctx.drawImage(img.qrcode,243*rate,937*rate,100*rate,100*rate) // 二维码
          ctx.drawImage(img.fingerprint,426*rate,937*rate,100*rate,100*rate) // 指纹
          ctx.drawImage(img.gou,190*rate, 1097*rate,24*rate,24*rate) // 勾
          ctx.drawImage(img.logo,275*rate,0*rate,200*rate,114*rate) // logo

          /**文字绘制*/
          // 商品标题
          ctx.setFillStyle('#333333')//文字颜色:默认黑色
          ctx.setFontSize(30*rate)  // 文字大小
          ctx.setTextAlign('left') // 文字居中
          textWrap(ctx,content.pTitle,103*rate, 858*rate,50*rate,660*rate)

          // 长按识别二维码 进入活动
          ctx.setFontSize(24*rate)
          ctx.setTextAlign('center') // 文字居中
          ctx.fillText(content.tip, 375*rate, 1084*rate)

          // 此二维码已安全认证,可以放心扫描
          ctx.setTextAlign('left') // 文字居中
          ctx.setFontSize(20*rate)
          ctx.setFillStyle('#88AE5C')//文字颜色:默认黑色
          ctx.fillText(content.scan, 220*rate, 1118*rate)

          // 活动标题
          // 活动标题由三部分组成:( 红色0 元抢购 原价 )这边需要根据原价进行偏移控制
          // 原价

          // 红色0
          let zeroDeviation = 0 // 红色字体偏移
          let zero = '0'
          let oirginDeviation = 0 // 原价偏移
          let oirgin = '原价¥0'
          // content.zero = 65650
          content.zero = content.zero.toString()  // 苹果手机必须字符串!!!!!!
          ctx.setFontSize(54*rate)
          let _zeroDeviation = parseFloat(ctx.measureText(zero).width)
          zeroDeviation = parseFloat(ctx.measureText(content.zero).width)
          zeroDeviation = (zeroDeviation-_zeroDeviation)/2
          if(shareInfo.cprice){ // 兼容旧数据没有cprice字段
            // content.oPrice = '原价¥043434'
            ctx.setFontSize(34*rate)
            ctx.setFillStyle('#666666')//文字颜色:默认黑色
            let _oirginDeviation = parseFloat(ctx.measureText(oirgin).width)
            oirginDeviation = parseFloat(ctx.measureText(content.oPrice).width)
            oirginDeviation = (oirginDeviation - _oirginDeviation)/2
            ctx.fillText(content.oPrice, 400*rate+zeroDeviation-oirginDeviation, 406*rate)
            // 绘制删除线
            ctx.beginPath();
            ctx.setStrokeStyle('#666666')
            ctx.setLineWidth(1)
            ctx.moveTo(506*rate+zeroDeviation-oirginDeviation, 392*rate)
            ctx.lineTo(506*rate+zeroDeviation+oirginDeviation, 392*rate)
            ctx.closePath();
            ctx.stroke()

            // 红色0
            ctx.setTextAlign('left')
            ctx.setFontSize(54*rate)
            ctx.setFillStyle('#FF0237')
            ctx.fillText(content.zero, 196*rate-zeroDeviation-oirginDeviation, 416*rate)


            // 元抢购
            ctx.setFontSize(44*rate)
            ctx.setFillStyle('#333333')
            ctx.fillText(content.mTitle, 248*rate+zeroDeviation-oirginDeviation, 410*rate)
            ctx.draw()
          }else {
            // 红色0
            ctx.setTextAlign('left')
            ctx.setFontSize(54*rate)
            ctx.setFillStyle('#FF0237')
            ctx.fillText(content.zero, 196*rate-zeroDeviation+50, 416*rate)
            // 元抢购
            ctx.setFontSize(44*rate)
            ctx.setFillStyle('#333333')
            ctx.fillText(content.mTitle, 248*rate+zeroDeviation+50, 410*rate)
            ctx.draw()
          }


          setTimeout(()=>{
            if(that.data.shareImgFlag){ // 如果弹窗了,但是画布还没生成,这提醒下
              wx.showToast({
                title: '生成海报中...',
                mask: true,
                icon: 'loading'
              })
            }
            let rate = 2
            wx.canvasToTempFilePath({
              x: 0,
              y: 0,
              width: 375,
              height: 667,
              destWidth: 375*rate,
              destHeight: 667*rate,
              canvasId: 'myCanvas',
              success: function (res) {
                that.setData({
                  resultimg: res.tempFilePath
                })
                wx.hideLoading()
                console.log('绘图成功')
              },
              fail: function () {
                // 导出图片错误
                /*wx.showModal({
                  title: '导出图片时出错',
                  content: '请重新尝试!',
                })*/
              },
              complete: function () {
                wx.hideLoading()
              }
            },that)
          },1000)
        }
      }
    })
  }

}

export default drawCanvas
总结:中间涉及到比较坑的是标题位移计算(发现数学能力现在倒退到小学水平了 lol),还有一个兼容问题:ctx.measureText(str)必须是字符串格式!!!开发过程发现str为number类型,苹果手机没有反应,安卓可以,坑得一批,找了大半天~
效果

你可能感兴趣的:(小程序画布canvas)