wxml2Canvas-小程序生成海报方案

3,029 阅读4分钟

wxml2Canvas库,可以将指定的wxml节点直接转换成canvas元素,并且保存成海报分享图。

可以在github查看相关的api。

详细的介绍、使用方式、原理等,可阅读以下文章:

我这里简单介绍使用方法:

一、安装

 npm install wxml2canvas

如果是原生微信小程序开发,需要构建npm

二、引入

import Wxml2Canvas from 'wxml2canvas'; 

三、数据初始化

在页面添加canvas元素

<!-- 此处的canvasStyle后面会讲 -->
<canvas :style="canvasStyle" canvas-id="canvasId" class="canvas"></canvas>

说明: canvasStyle:绑定style的写法是uniapp项目,如原生小程序开发项目需修改。绑定的样式是canvas元素的宽高,后面会说明

canvasId:canvas 组件的唯一标识符

// 此处样式是让canvas元素不在可视范围内
.canvas {
    position: fixed;
    top: -9999999rpx;
    background: #fff;
}

3.1 创建实例

let that = this
let drawImage = new Wxml2Canvas(
    {
      element: 'canvasId', // canvas节点的id,
      obj: that, // 在组件中使用时,需要传入当前组件的this
      width: that.wxmlCanvas.width, // 宽高
      height: that.wxmlCanvas.height, // 这里的高度要动态获取
      background: '#fff', // 默认背景色
      progress() {
        // 绘制进度
      },
      async finish(tempFilePath) {
        console.log(tempFilePath)

      },
      error(err) {
        console.log(err, 'err')
      }
    },
    // 在组件中使用时,需要传入当前组件的this
    that
  )

3.2 传入数据

let data = {
    //直接获取wxml数据
    list: [
      {
        type: 'wxml',
        // 第一个参数是绘制对象的根元素的类名,第二个参数是需要绘制节点的类名
        class: '.canvas_ele_limit, .draw_canvas',
        limit: '.canvas_ele_limit',// 绘制对象的根元素的类名
        x: 0,
        y: 0
      }
    ]
  }

如上,type声明为wxml时,会查找所有类名为draw_canvas的节点,并且加入到绘制队列中。
class传入的第一个类名(canvas_ele_limit)限定了查询的范围,可以不传,第二个用来指定查找的节点,可以定义为任意不影响样式展现的通用类名。

3.3 绘制

// 在组件中使用时,需要传入当前组件的this
drawImage.draw(data, this)

3.4完整版代码

// uniapp写法
export default {
    data(){
        绘制对象元素的宽高
        wxmlCanvas: {},
        // canvas元素的style值
        canvasStyle: ""
    }
    onLoad() {
        this.$nextTick(() => {
          setTimeout(async () => {
            // 获取绘制元素的宽高
            const htmlWidthHeight = await this.getWidthHeight('#wxml-canvas')
            Object.assign(this.wxmlCanvas, {
              width: Math.floor(htmlWidthHeight.width),
              height: Math.floor(htmlWidthHeight.height)
            })
            // 设置canvas元素的宽高
            this.canvasStyle = `width: ${htmlWidthHeight.width}px;height: ${htmlWidthHeight.height}px`
            setTimeout(() => {
              this.draw()
            }, 100)
          }, 300)
        })
    },
    methods: {
        // 获取节点的宽高
        getWidthHeight(selector) {
          return new Promise((resolve) => {
            const query = uni.createSelectorQuery().in(this)
            query
              .select(selector)
              .boundingClientRect((data) => {
                return resolve(data)
              })
              .exec()
          })
        },
        draw() {
          let that = this
          let data = {
            //直接获取wxml数据
            list: [
              {
                type: 'wxml',
                // 第一个参数是绘制对象的根元素的类名,第二个参数是需要绘制节点的类名
                class: '.canvas_ele_limit, .draw_canvas',
                limit: '.canvas_ele_limit',// 绘制对象的根元素的类名
                x: 0,
                y: 0
              }
            ]
          }
           //创建wxml2canvas对象
          let drawImage = new Wxml2Canvas(
            {
              element: 'canvasId', // canvas节点的id,
              obj: that, // 在组件中使用时,需要传入当前组件的this
              width: that.wxmlCanvas.width, // 宽高
              height: that.wxmlCanvas.height, // 这里的高度要动态获取
              background: '#fff', // 默认背景色
              progress() {
                // 绘制进度
              },
              async finish(tempFilePath) {
                console.log(tempFilePath)

              },
              error(err) {
                console.log(err, 'err')
              }
            },
            that
          )
          //传入数据,画制canvas图片
          setTimeout(() => {
            drawImage.draw(data, that)
          }, 30)
        },
    }
}

四、页面元素的绑定

需要绘制的元素都要表明data-type类型,以及绘制时查找结点的类名draw_canvas

4.1 文字元素

如果是文字,需要给元素绑定data-type="text"data-text="需要绘制的文字内容

<view data-type="text" data-text="文字1" class="draw_canvas goods-info__car-calc_item-label">文字1</view>

4.2 图片元素

如果是图片,需要给元素绑定data-type="image"data-url="图片地址"

<image data-type="image" data-url="图片地址" class="draw_canvas goods-info__employee-qrcode" src="图片地址"></image>

4.2 页面使用

<view class="box">
    <!-- 画图区域 -->
    <view id="wxml-canvas" class="canvas_ele_limit">
        <view class="draw_canvas poster-wrapper">
            <view data-type="text" data-text="文字1" class="draw_canvas goods-info__car-calc_item-label">文字1</view>
            <view class="draw_canvas poster-wrapper_top">
                <image data-type="image" data-url="图片地址" class="draw_canvas goods-info__employee-qrcode" src="图片地址"></image>
            </view>
        </view>
    </view>
    <!-- 画布 -->
    <canvas :style="canvasStyle" canvas-id="canvasId" class="canvas"></canvas>
</view>

五、所遇到的问题

绘制失败的原因如下: image.png

5.1 图片保存失败:errorcode:1000

出现如下报错,是图片保存失败 企业微信截图_16684934924888.png 如果调试时,发现获取组件this,获取元素宽高,以及传递至wxml2Canvas库中的参数都成功了,但是仍然报错:.canvasToTempFilePath: fail canvas is empty,如下图所示: image.png 是因为调用wxml2Canvas调用wx.canvasToTempFilePath()返回的错误。

此时可能是微信开发者功能版本过高,切换低版本即可解决。 image.png

5.2 图片保存失败:errorcode: 1001

如果有使用背景图,即元素设置data-type="background-image",可能会失败。此时将背景图改成图片,就可以解决,即:

<image data-type="image" data-url="图片地址" class="draw_canvas goods-info__employee-qrcode" src="图片地址"></image>

5.3 绘制图片保存至相册会有白边

如图所示: image.png

如果底部是背景色,则可以直接在创建wxml2canvas对象设置background为背景颜色即可。 如果是背景图片,则不可以。

解决方案:

// 设置page元素的width:100%
page {
    width: 100%;
    height: 100%;
}

// 设置绘制页面的根元素的width:100%
.box {
    width: 100%;
}

5.5 网络图片绘制失败:errorcode:1000

如果页面有网络图片,调试源码可知,绘制进度一直在30%,导致出现errorcode:1000的错误。如果此时打开调试工具vconsole,会绘制成功。因为调试状态下,不会有域名限制。出现这种情况就是没有配置白名单的原因。 必须在小程序管理后台配置白名单

出现问题的原因是:在绘制图片时,使用了wx.getImageInfo方法去获取图片的信息,从文档可知,需要配置白名单。 image.png

白名单配置路径:开发=>开发管理=>开发设置=>服务器域名=>downloadFile合法域名

image.png image.png