微信小程序-canvas2D-海报问题记录

3,821 阅读9分钟

微信小程序-canvas2D-海报-安卓适配

前言

官方文档一言难尽,本文记录在canvas2D实际体验中遇到的各种问题及解决方案

canvas2D

使用原因:官方宣布:2.9.0 起支持一套新 Canvas 2D 接口(需指定 type 属性),同时支持同层渲染,原有接口不再维护。 使用:

 <canvas type="2d" id="myCanvas"></canvas>

注意点:

  • id而不是canvas-id
  • 注意声明type="2d"
  • wx.canvasToTempFilePath 真机调试失效,暂无具体解决办法,官方维护人员:走预览调试,真机无效,具体原因:官方就是这么屌
  • 在使用```canvasToTempFilePath``将cavnas转换成图片时,canvas一定要出现在页面结构中,否则会出现fail no image报错情况,另外一定要赋予其宽度和高度,不要使用%写法,如下:(使用vw,vh,px都行)
<!-- BAD -->
<canvas type="2d" id="myCanvas" style="width:100%;height:100%;"></canvas>
<!-- GOOD -->
<canvas type="2d" id="myCanvas" style="with:100vw;height:100vh;"></canvas>
  • ctx.drawImage()使用如下:
const img = canvas.createImage()
img.src = that.data.erCodeImage.path
img.onload = () => {
ctx.drawImage(img, 540 * unit, 860 * unit, 165 * unit, 165 * unit)
}
  • 注意网络图片需为https,并且需将图片下载到本地,方法如下:
/**
     * @description: 封装的下载图片函数
     * @params: 
     * @return {type} 
     */
    downLoadImage(url) {
      return new Promise((resolve, reject) => {
        // console.log("要下载的图片", url)
        wx.getImageInfo({
          src: url,
          success(res) {
            // console.log("下载好的", res.path)

            // resolve(res.path)
            resolve(res)
          },
          fail(err) {
            reject(err)
          }
        })
      })
    },
    
    /**
     * @description: 封装的下载云存储文件函数
     * @params: 
     * @return {type} 
     */
    downLoadCloudFile(id) {
      return new Promise((resolve, reject) => {
        wx.cloud.downloadFile({
          fileID: id,
          successres => {
            // 返回临时文件路径
            console.log(1231312312, res)
            resolve(res.tempFilePath)
          },
          failerr => {
            console.log('err', err)
          }
        })
      })
    },
  • 一些setFillStyle的使用方法有修改,具体使用百度即可

生成海报项目开始

构建画布

<canvas type="2d" id="myCanvas" style="width750px;height1125px;border:1px solid blue;;margin:auto;"></canvas>

canvas元素,同html中的canvas

初始化canvas

  • 创建一个dom元素节点查询器
// 页面中
const query = wx.createSelectorQuery()
// 组件中
const query = wx.createSelectorQuery().in(this)
  • 选择我们的canvas节点
query.select('#myCanvas')
  • 初始化canvas画布的基础参数

这里着重注意:dpr设备像素比,为了适配各种各样的安卓机,这里推荐使用dpr=1,否则,会出现如下类似报错 canvasToTempFilePath:fail:convert native buffer parameter fail. native buffer exceed size limit,

initCanvas() {
    const query = wx.createSelectorQuery().in(this// 创建一个dom元素节点查询器
    query.select('#myCanvas'// 选择我们的canvas节点
    .fields({ // 需要获取的节点相关信息
        nodetrue// 是否返回节点对应的 Node 实例
        sizetrue // 是否返回节点尺寸(width height)
    }).exec((res) => { // 执行针对这个节点的所有请求,exec((res) => {alpiny})  这里是一个回调函数
        const dom = res[0// 因为页面只存在一个画布,所以我们要的dom数据就是 res数组的第一个元素
        const canvas = dom.node // canvas就是我们要操作的画布节点
        const ctx = canvas.getContext('2d'// 以2d模式,获取一个画布节点的上下文对象
        // const dpr = wx.getSystemInfoSync().pixelRatio // 获取设备的像素比,未来整体画布根据像素比扩大
        const dpr = 1
        // dpr大小影响图片的显示分辨度,可以理解为影响像素的大小,值越大,越高清,但个人猜测过大会影响渲染效率或者加重小程序运行负担
        // const unit = 2
        canvas.width = 750 * dpr
        canvas.height = 750 * 1.5 * dpr
        ctx.scale(dpr,dpr)
        this.setData({
        canvasDom: dom, // 把canvas的dom对象放到全局
        canvas: canvas, // 把canvas的节点放到全局
        ctx: ctx, // 把canvas 2d的上下文放到全局
        dpr: dpr // 屏幕像素比
        }, function () {
        console.log('开始绘图')
        this.drawWork() // 开始绘图
        })
    })
},

关于canvas.width和canvas.height设置,主要是为了能方便控制画出图像的大小,canvas的width和height并不是style中的width和height,换句话说就是前者是==画布宽高==后者是==css元素宽高== ctx:指2d上下文,通俗点理解可以理解为画图的画笔

  • 绘制文本时特别要注意,字体一定要是系统中有的字体,否则文字绘制不上,部分安卓机型可能会闪退,这里推荐如下代码操作,推荐字体:Arial,如果文字出现真机中变小问题,可能原因如下:
  1. 注意font中的属性值设置顺序,错误的顺序可能会带来不一样的结果
  2. 有些可能是因为未设置字体导致,具体不详
ctx.font = '56px Arial';
// BAD
// 设置粗体文字
ctx.font = '56px bold Arial';
// GOOD
// 设置粗体文字
ctx.font = 'bold 56px Arial';
  • 在使用canvasToTempFilePath将图片转成临时文件保存到手机时注意设置输出宽度(destWidth),输出高度(destWidth),否则在手机上查看时,你会发现图片被拉伸了或者某方向被裁剪了,具体可详见canvas文档中的canvasToTempFilePath

setData()设置canvas,ctx

关于在安卓机中将canvas,ctx对象赋值给data时使用setData()报错 推荐如下设置(直接赋值给this对象,js中一样可以全局使用)

// BAD
query.select('#myCanvas'// 选择我们的canvas节点
      .fields({ // 需要获取的节点相关信息
        nodetrue// 是否返回节点对应的 Node 实例
        sizetrue // 是否返回节点尺寸(width height)
      }).exec((res) => { 
        const dom = res[0// 因为页面只存在一个画布,所以我们要的dom数
        const canvas = dom.node // canvas就是我们要操作的画布节点
        const ctx = canvas.getContext('2d'// 以2d模式,获取一个画布
        // 这种操作会报错
        that.setData({
          canvasDom:dom
          canvas:canvas
          ctx: ctx
        })
        // this.drawWork()
      })
// GOOD
query.select('#myCanvas'// 选择我们的canvas节点
      .fields({ // 需要获取的节点相关信息
        nodetrue// 是否返回节点对应的 Node 实例
        sizetrue // 是否返回节点尺寸(width height)
      }).exec((res) => { 
        const dom = res[0// 因为页面只存在一个画布,所以我们要的dom数
        const canvas = dom.node // canvas就是我们要操作的画布节点
        const ctx = canvas.getContext('2d'// 以2d模式,获取一个画布
        // 安卓机用setData会出现报错,所以直接保存在that中
        that.canvasDom = dom
        that.canvas = canvas
        that.ctx = ctx
        // this.drawWork()
      })

drawImage绘制很多图片

canvas绘制过多图片时,在保存到手机的图片中可能会有部分图片不显示,具体原因是因为图片未全部绘制完成就开始生成图片,这里需要进行异步处理,在图片全部绘制完成后再进行生成和保存