小程序 Canvas 2D 爬坑

微信小程序声称 2.9.0 起支持新的 Canvas 2D 接口,且官方推荐,使用性能更好的2d模式。

官网文档地址 https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html
新的 canvas 2d,接口与 Web 一致,然而,官方的文档上对新 api 的说明寥寥无几,只能翻看 Web端api。

wxml

<canvas type="2d" id="canvas" style="width: 100%;height: 400rpx"/>

wxml 中就是简单加个 id、type,以及样式。但是官方的 bug 提示中
Bug & Tip

  1. tip: Canvas 2D(新接口)需要显式设置画布宽高 (默认为 300x150)

这里有个细节,后续会用。先按照正常流程往后走,取 ctx 对象

drawCanvas(){
    const query = wx.createSelectorQuery()
    query.select('#canvas')
      .fields({ node: true, size: true })
      .exec((res) => {
        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)

        ctx.fillRect(0, 0, 100, 100)
      })
}

标签里定义了宽高,在这里又重新定义,难以理解,试着删除 width、height 的设置。
然而后面的操作出现了变形,即使绘入一个正方形,在画布上展示的比例也不对。打印出 canvasNode,如下

通过 style 设置的宽高 _width: 375,_height: 195;实际宽高 width: 300,height: 150,显然来自默认宽高。
回想一下前面的官方bug 提示

  1. tip: Canvas 2D(新接口)需要显式设置画布宽高 (默认为 300x150)

大胆设想一下,style设置宽高,相当于设置组件的外部容器,js设置宽高,则是定义实际操作的画布的宽高。。。
此外,dpr 设置像素比,缩放坐标系,把当前的坐标系横纵轴放大、缩小到 drp 倍。官方demo中,采用的是设备像素比,旨在达到最佳像素尺寸的图。
项目中,实际使用时,更多是750设计稿,不妨将其缩放比设为屏幕宽度/750标准宽度,此后的所有尺寸,都可以按照750设计稿上的尺寸来写入。改写一下

async drawCanvas(){
    // 取 canvas 节点
    // 此处使用 await
    let canvasNode = await new Promise((resolve, reject)=>{
      // 注意:若canvas是定义在在组件内,需改用
      // const query = wx.createSelectorQuery().in(this)
      const query = wx.createSelectorQuery()
      query.select('#canvas')
        .fields({ node: true, size: true })
        .exec(res=>{
          resolve(res[0])
        })
    })
    let canvas = canvasNode.node,
    ctx = canvas.getContext('2d')
    this.canvas = canvas
    this.ctx = ctx
    // 获取设备像素比调整画布尺寸,并缩放坐标系
    const dpr = wx.getSystemInfoSync().screenWidth / 750
    let width = canvasNode.width * dpr
    let height= canvasNode.height* dpr
    canvas.width = width
    canvas.height = height

    // 设置 canvas 坐标原点
    ctx.translate(width/2, height * 2 / 3);
    ctx.scale(dpr, dpr)
}

此处,ctx.translate()用来调整 canvas 坐标系,默认是左上角,ctx.translate(width/2, height * 2 / 3)可设置坐标系原点为横向中心,纵向2/3高度处。可根据实际应用做调增。
到这里,初步完成 canvas 的前期初始化,后面即可绘制

async render () {
    let canvas = this.canvas
    let ctx = this.ctx
    // 实例化 canvas 图像
    let image = await new Promise((resolve)=>{
      const img = canvas.createImage()
      img.src = '../images/img-arrow.png'
      img.onload = () => {
        resolve(img)
      }
    })

    let angle = -45    // 实际角度
    let abs = 1        // 正反转

    // 循环执行
    const renderLoop = () => {
      if(angle>45){
        abs = -1
      }else if(angle<-45){
        abs = 1
      }

      // 清楚画布
      ctx.clearRect(-375, -400, 750, 750)
      // 保存当前帧,以备复原
      ctx.save()
      // 画布旋转
      ctx.rotate(degree / 180 * Math.PI);
      // 绘制图像
      ctx.drawImage(image, -110, -228, 221, 302)
      // 操作完后复原至 save 步骤
      ctx.restore()
        
      angle += abs
    }
    // 动画帧循环执行
    canvas.requestAnimationFrame(()=>{
      renderLoop()
    }) 
}

参考源码:wxapp_components/poster

posted @ 2020-06-18 17:28  晨の风  阅读(7011)  评论(2编辑  收藏  举报