wxml-to-canvas 使用问题

1,098 阅读4分钟

wxml-to-canvas | 微信开放文档 (qq.com)

引入

  • 在当前目录的控制台上输入如下代码:
npm install --save wxml-to-canvas

  • 点击微信小程序左上角的《工具》->构建npm

    注意:若项目之前没有使用过npm管理依赖是无法构建npm的,需要在项目根目录的控制台输入如下代码初始化npm工程。

npm init

  • 引入该组件
"usingComponents": {
    "wxml-to-canvas": "wxml-to-canvas"
  }

  • 使用该组件
<wxml-to-canvas class="widget" width="375" height="550"></wxml-to-canvas>

注意点:

  • class属性用来标识该组件,可通过设置不同的值来创建不同的wxml-to-canvas组件实例
//在页面js文件中获取该组件实例
onload:function() {
    this.widget = this.selectComponent('.widget')
}
  • 上面的语句this.widget = this.selectComponent('.widget')是完成widget对象初始化的,但canvas生成是需要一定时间的,onload函数中widget对象还没有初始化完毕就去调用this.widget.renderToCanvas({wxml,style})将wxml模板和wxss样式绘制到canvas上 的话,会导致页面报如下错: image.png

  • 解决办法:

进行延迟,给含有this.widget.renderToCanvas({wxml,style})的代码块加一个定时器,500毫秒后再执行,之后就OK了

width和height的值不能省略。

规避点

所有的宽高文字大小使用设计稿大小

如果只需要一次性使用,可以在第一次生成成功之后直接生成图片,然后将图片放在指定位置,根据需要布置图片大小。并移除组件和js缓存。

这样可以减少小程序的性能消耗

renderToCanvas参数必须有wxml和style

调用 this.widget.renderToCanvas({wxml,style}) 必须有wxml和style,最好将wxml 和 style写在方法内,最终返回{wxml,style}

class不能-结尾

wxml中的class如果-结尾会报错

元素要指定width height

每个元素都必须指定 width 和 height 属性,否则会导致布局错误。

文字要写在text组件中

不写在text中无法正常显示

当使用 verticalAlign: 'middle'元素找不到

父元素高30,text元素高30时候,这时是看不到文字的;需要text元素高度也变成40才能看到文字。另外垂直居中也可以使用 lineHeight: 30

不确定文字宽度计算

  • 准备工作
    • 数据存储格式
    viewData: {
        size: 0,
        text: ''
    }
    
    • 页面显示
      <view class="view-text" style="font-size: {{viewData.size}}px; line-height: {{viewData.size}}px;">{{viewData.text}}</view>
    
    • 调用方法
      getTextWidth (text, size) {
          return new Promise((resolve, reject) => {
              this.setData({
                  viewData: {
                      size,
                      text
                  }
              }, () => {
                  wx.createSelectorQuery()
                      .select('.view-text')
                      .boundingClientRect()
                      .exec((res) => {
                          resolve(res[0])
                      })
              })
          })
      },
    
    • 使用方法
      const data = await getTextWidth('28.88'.replaceAll('1', '0'), 32)
      // 因为不能使用行内样式,因此只能给一个class,当遇到循环创建的时候可以给一个下标,使用class="goods-num-0"
      goodsNum0: {width: data.width},
    

金额划线价

首先需要用上面方法计算一个宽度,然后通过定位一条灰色的线画到文字中间

读取图片大小

可以通过 wx.getImageInfo() 读取图片大小,进行排版

画边框

当使用borderColor 指定边框颜色的时候,同时背景色也会被指定,这时需要重新设定背景色就好了

文字省略号

计算文字长度,超过部分截取使用...替换

画虚线

未解决,使用切图解决

针对组件速度优化

优化图片生成速度

在调用组件canvasToTempFilePath方法生成图片临时文件时,在其内部调用的是wx.canvasToTempFilePath方法,但是调用方法中的destWidthdestHeight参数是固定的,是canvas大小乘以屏幕的pixelRatio,所以尝试减小输出图片大小优化加载速度。

优化后代码:增加自定义destWidthdestHeight参数

canvasToTempFilePath(args = {}) {
    const use2dCanvas = this.data.use2dCanvas

    return new Promise((resolve, reject) => {
    const {
        top, left, width, height
    } = this.boundary

    const copyArgs = {
        x: left,
        y: top,
        width,
        height,
        destWidth: args.destWidth || width * this.dpr,
        destHeight: args.destHeight || height * this.dpr,
        canvasId,
        fileType: args.fileType || 'png',
        quality: args.quality || 1,
        success: resolve,
        fail: reject
    }

    if (use2dCanvas) {
        delete copyArgs.canvasId
        copyArgs.canvas = this.canvas
    }
    wx.canvasToTempFilePath(copyArgs, this)
    })
}

使用代码

const res = await widget1.canvasToTempFilePath({
    destWidth: baseWidth,
    destHeight: baseHeight
})

这种方法只有在生成图片大小变小的时候才会有效,因此只在特定场景有效

添加初始化事件

当页面通过selectAllComponents获取了组件,紧接着去调用组件renderToCanvas方法去绘制图片,这时候可能会出现组件内部参数未初始化完成,调用失败的情况,因此需要一个组件加载成功的事件。

如果使用setTimeout方法等待组件完成后再去执行后面的代码,这样可能会导致等待的时间过长,时效性不高。

优化后代码:

if (use2dCanvas) {
    const query = this.createSelectorQuery()
    query.select(`#${canvasId}`)
    .fields({node: true, size: true})
    .exec(res => {
        const canvas = res[0].node
        const ctx = canvas.getContext('2d')
        canvas.width = res[0].width * dpr
        canvas.height = res[0].height * dpr
        ctx.scale(dpr, dpr)
        this.ctx = ctx
        this.canvas = canvas
        this.triggerEvent('ready')
    })
} else {
    this.ctx = wx.createCanvasContext(canvasId, this)
    this.triggerEvent('ready')
}

使用代码

<wxml-to-canvas
    bind:ready="handleReady"
/>

支持fontWeight

这个文件

这个文件

这个方法

这个方法

然后就可以用了

然后就可以用了

亮点

精确计算文字占用宽度

ctx.measureText(text).width

想要精准计算宽度还要指定字体的样式才可以,比如:

ctx.font = `${fontWeight} ${fontSize}px sans-serif`

参考

wxml-to-canvas没有fontWeight的相关支持? | 微信开放社区 (qq.com)