记录:uni-app多端小程序使用canvas绘制图片

  • 对于canvas我一直都是只是了解状态,只是知道大概要怎么画,没有具体的项目实战经验,最近有个需求就是要用canvas画个分享的海报。看起来也不难,实际画起来还是花了不少实际,也踩了一些小坑,所以记录一下
  • 整理一下使用的api
    • fillText 写文字
    • fillStyle 用来设置字体颜色
    • setFontSize 字号
    • font 字符串,设置字体所有格式,字号和字体家族必填
    • fillRect 画个长方形
    • setFillStyle 填充颜色
    • setStrokeStyle 设置画笔颜色
    • moveTo 画笔起始点
    • lineTo 画笔移动点
    • stroke 绘制
    • arc画圆
    • clip 裁剪
    • drawImage 绘制图片
    • draw 绘制

我用到的所有方法都在上面了,本质上用画布作图可以理解为全部用css定位来布局,所已写起来很无脑。

踩坑点

我在使用drawImage把图片绘制到canvas上的时候是正常的,但是当使用canvasToTempFilePath导出成图片的时候就不正常了,里面的图片都没显示,其他文字线条都是正常的,这就很纳闷了,网上查了很多也没发现,后来观察代码猜想可能是由于是异步下载图片造成的,使用drawImage步骤是这样的:

  • 使用getImageInfo获取图片的本地路径
  • 成功后使用drawImage绘制图片

所以问题可能是getImageInfo是异步操作,图片还没获取成功就开始导出了,还是要细心点

完整代码

/**
 * @description: 绘制分享海报
 * @param {String} canvasId
 * @param {Object} opt 绘制参数,包含:
    *      kuaidiComLogo
    *      kuaidiComName
    *      kuaidiNum
    *      tabIdName
    * 
    *      sendxzq
    *      sendcity
    *      sendaddr
    *      sendName
    *      sendmobile
    *  
    *      recxzq
    *      reccity
    *      recaddr
    *      recName
    *      recmobile  
 * @param {Number} type 海报样式 默认1
 * @return {Promise} 图片url
 * 
 * example:
 * import { createShareCanvas } from'@/utils'
 * 
 * this.shareImg = await createShareCanvas('shareImg', {...}, 2)
 */
export function createShareCanvas(canvasId, opt = {}, type = 1) {
  return new Promise(async(resolve, reject) => {
    if(!canvasId){
      reject(new Error('请传入canvasId'))
    }
    const ctx = uni.createCanvasContext(canvasId)
    // 顶部
    ctx.setFillStyle('#317ee7')
    ctx.fillRect(0, 0, 420, 70)
    ctx.setFontSize(24)
    ctx.fillStyle = '#fff'
    if(opt.kuaidiNum) {
      await new Promise((resolve, reject) => {
        uni.getImageInfo({
          src: opt.kuaidiComLogo,
          success(img) {
              console.log(img);
              ctx.save()
              ctx.beginPath()
              ctx.arc(38, 33, 18, 0, 2 * Math.PI)
              ctx.clip()
              ctx.drawImage(img.path, 20, 15, 36, 36)
              ctx.restore()
              ctx.fillText(`${opt.kuaidiComName} ${opt.kuaidiNum}`, 70, 43)
              ctx.draw(true)
              resolve()
          },
          fail(err) {
            reject(err)
          }
        });
      })
    } else {
      ctx.fillStyle = '#98BEF3'
      ctx.fillText('暂无快递单号', 20, 43)
    }

    // 中间信息,根据type展示不同样式
    if(type === 1) {
      console.log(ctx.measureText(opt.tabIdName))
      ctx.fillText(opt.tabIdName, 338, 43)
      // 收寄件人
      ctx.setFillStyle('#f5f6f8')
      ctx.fillRect(0, 70, 420, 224)
      ctx.font = 'italic small-caps bold 12px/1'
      ctx.fillStyle = '#333'
      ctx.fillText(`${opt.sendName} ${opt.sendmobile}`, 75, 103.5)
      // ctx.fillText(opt.sendmobile, 75 + ctx.measureText(opt.sendName).width + 15, 103.5)
      ctx.fillText(`${opt.recName}`, 75, 195)
      ctx.fillText(opt.recmobile, 75 + ctx.measureText(opt.recName).width + 15, 195)
      // 地址
      ctx.fillStyle = '#878787'
      ctx.setFontSize(20)
      let sendFullAddr = opt.sendxzq + opt.sendaddr
      if(sendFullAddr.length <= 14) {
        ctx.fillText(sendFullAddr, 75, 142)
      } else {
        ctx.fillText(sendFullAddr.substr(0, 14), 75, 131)
        ctx.fillText(sendFullAddr.substr(14), 75, 159)
      }
      let recFullAddr = opt.recxzq + opt.recaddr
      if(recFullAddr.length <= 14) {
        ctx.fillText(recFullAddr, 75, 233)
      } else {
        ctx.fillText(recFullAddr.substr(0, 14), 75, 233)
        ctx.fillText(recFullAddr.substr(14), 75, 261)
      }
      ctx.setStrokeStyle('#e2e2e2')
      ctx.moveTo(75, 166)
      ctx.lineTo(400, 166)
      ctx.stroke()
      
      const p1 = new Promise((resolve, reject) => {
        uni.getImageInfo({
          src: 'https://cdn.kuaidi100.com/images/wxapp/kuaidi100/ico-ji.png',
            success(img) {
                console.log(img);
                ctx.drawImage(img.path, 20, 92, 28, 34)
                // ctx.draw(true)
                resolve()
            },
            fail(err) {
              reject(err)
            }
        });
      })
      const p2 = new Promise((resolve, reject) => {
        uni.getImageInfo({
          src: 'https://cdn.kuaidi100.com/images/better/arrow_down.png',
            success(img) {
                console.log(img);
                ctx.drawImage(img.path, 24, 135, 18, 37)
                // ctx.draw(true)
                resolve()
            },
            fail(err) {
              reject(err)
            }
        });
      })
      const p3 = new Promise((resolve, reject) => {
        uni.getImageInfo({
          src: 'https://cdn.kuaidi100.com/images/wxapp/kuaidi100/ico-shou.png',
            success(img) {
                console.log(img);
                ctx.drawImage(img.path, 20, 183, 28, 34)
                // ctx.draw(true)
                resolve()
            },
            fail(err) {
              reject(err)
            }
        });
      })
      await Promise.all([p1,p2,p3]).catch(err => console.log(err))
    } else if(type === 2) {
      ctx.fillStyle = '#333'
      ctx.setFontSize(30)
      ctx.fillText(opt.sendcity, 105-ctx.measureText(opt.sendcity).width/2, 165)
      ctx.fillText(opt.reccity, 315 - ctx.measureText(opt.reccity).width/2, 165)
      ctx.setStrokeStyle('#e2e2e2')
      ctx.moveTo(173, 155)
      ctx.lineTo(193, 155)
      ctx.stroke()
      ctx.moveTo(228, 155)
      ctx.lineTo(248, 155)
      ctx.stroke()

      ctx.fillStyle = '#878787'
      ctx.setFontSize(24)
      ctx.fillText(opt.sendName, 105-ctx.measureText(opt.sendName).width/2, 215)
      ctx.fillText(opt.recName, 315 - ctx.measureText(opt.recName).width/2, 215)

      ctx.fillStyle = '#317EE7'
      ctx.setFontSize(24)
      ctx.fillText(opt.tabIdName, 210-ctx.measureText(opt.tabIdName).width/2, 215)
      await new Promise((resolve, reject) => {
        uni.getImageInfo({
          src: 'https://cdn.kuaidi100.com/images/wxapp/kuaidi100/plane.png',
          success(img) {
              console.log(img);
              ctx.drawImage(img.path, 198, 143,24, 22, )
              ctx.draw(true)
              resolve()
          },
          fail(err) {
            reject(err)
          }
        });
      })
    }

    // 底部时间
    ctx.fillStyle = '#fff'
    ctx.fillRect(0, 274, 420, 43 )
    ctx.fillStyle = '#333'
    ctx.fillText(`寄出时间:${opt.lastupDate}`, 20, 304)
    
    setTimeout(() => {
      ctx.draw(true, () => {
        uni.canvasToTempFilePath({
          x: 0,
          y: 0,
          width: 420,
          height: 337,
          destWidth: 420,
          destHeight: 337,
          canvasId,
          success: function(res) {
            // 在H5平台下,tempFilePath 为 base64
            console.log(res.tempFilePath)
            resolve(res.tempFilePath)
          } ,
          fail(err) {
            console.log(err)
            reject(err)
          }
        })
      })
      
    }, 50)
  })
}

效果图

记录:uni-app多端小程序使用canvas绘制图片_第1张图片

你可能感兴趣的:(vue,微信小程序,uni-app,javascript)