小程序生成分享图神器 wxml2canvas


最近有遇到一个需求,需要动态生成海报图片,而小程序中,生成图片必然绕不开canvas,为了开发效率,再让我去造已有的轮子已然是不现实,时间成本也不允许,所以我调研了目前已有的一些轮子,发现还是wxml2canvas更简单易用,今天我就记录一下使用的一些方法和踩过的坑吧

一、官方的方案和wxml2canvas的优劣

有一款针对web端的生成图片或其他格式的解决方案,是使用**html2canvas,但是它是需要操作dom节点生成节点列表,而小程序是没有dom这一概念的,微信官方有推出一款用于将模板转成canvas最后转成图片的idea,名为:wxml-to-canvas,但!本人亲测,非常难以使用。首先:我的页面样式非常复杂,涵盖了动态高度,动态内容变化,各种定位及很多的图片,这就是wxml-to-canvas所不能实现的,你要说页面是个写死的静态海报,那没问题,我还是非常推荐这个的,因为你就写个静态模板,用于生成canvas**,最后转成图片!

那么针对以上我所说的,还有文字换行、表情文字图片混排、子标题等元素都要一一绘制等等,使用wxml2canvas就是不二选择。实现一个通过wxml节点标记,收集元素从而进行编译转换,仅依赖wxml直出需要绘制的canvas进而快速生成分享图。

二、实现原理

它包含了对块级元素、行内元素、背景色等简单样式的转换,达到:wxml -> canvas -> image 的一次性处理,且不需要重复书写静态代码模板进行编译。

小程序提供了如下特性、可供我们便捷使用:

  • measureText 接口能直接测量文本宽度;
  • SelectorQuery可以查询到节点对应的computedStyle。

同时小程序也存在一些弊端,比如:

  • 通过SelectorQuery只能拿到节点的style,而无法获取文本节点的内容
  • canvas在调试时可能置于最顶层,影响效果

所以我们第一步获取元素就面临两个问题:

  1. 如何获取需要转成canvas的元素
  2. 如何拿到获取元素的对应属性(样式、节点、内容…)

当获取到待收集的元素后,就可以将元素绘制到指定的canvas上,也就实现了wxml2canvas

三、如何使用

1.可以通过npm构建wxml2canvas:

npm install wxml2canvas

2.引入

import Wxml2Canvas from 'wxml2canvas'; 

3.使用

html


<canvas canvas-id="share"></canvas>
 
<view class="share__canvas share__canvas1">
    <view class="share__canvas1-text draw_canvas" 
        data-type="text" data-text="这是一段无边距文字">
        这是一段无边距文字
    </view>
</view>

如果需要使用到图片,其中data-url是图片在canvas显示的地址,即:

<image class="draw_canvas" 	data-type="image" 
    data-url="/static/share/friend_logo.png" 
    data-delay="1" mode="aspectFit" src="/static/share/friend_logo.png"></image>

其中,还有text标签可以使用,必填属性为data-type,声明此标签的在canvas中展示类型,每个标签都有data-top和data-left的属性,分别代表元素参照与自身位置的偏移值。 需要注意的是,data-text是生成的canvas中显示的文字,可以理解为标签的自定义属性,也可以接受变量等。

写入事件,用于获取id为wxml-canvas的宽高,这里的获取的元素的宽高就是将来生成canvas的父元素,获取之后触发draw函数。

//动态获取画制作元素的宽高
drawCanvas: function () {
    const query = wx.createSelectorQuery().in(this);
    query.select('#wxml-canvas').fields({
        size: true,
        scrollOffset: true
    }, data => {
        let width = data.width;
        let height = data.height;
        this.width = width;
        this.height = height;
        setTimeout(() => {
            this.draw()
        }, 1500);
}).exec();

初始化

/**
 * element:需要渲染的canvas节点
 * class:查找所有类名为exc-c的节点,并进行加入绘制队列
 * limit:限定相对位置 
 *
 **/
wxml2Canvas({
  element: "over-canvas",
  options: {
    class: ".exc-c",
    limit: ".limit-r",
  },
})



draw() {
    let that = this
 
    //创建wxml2canvas对象
    let drawImage = new Wxml2Canvas({
      element: 'share', // canvas节点的id,
      obj: that, // 在组件中使用时,需要传入当前组件的this
      width: this.width * 2, // 宽高
      height: this.height * 4,
      background: '#fff', // 默认背景色
      progress(percent) { // 绘制进度
      },
      finish(url) {
        console.log("创建的图片", url);
      },
      error(res) {
        console.log(res);
        // uni.hideLoading()
        // 画失败的原因
      }
    }, this);
    let data = {
      //直接获取wxml数据
      list: [{
          type: 'wxml',
          class: '.long_friend  .draw_canvas',
          limit: '.long_friend ',
          x: 0,
          y: 0
        } ]
    }
  //传入数据,画制canvas图片
  drawImage.draw(data, this);
},
创建wxml2canvas对象

这一步是为了创建wxml2canvas对象,其中传入的参数有三个,第一个参数为一个对象option。第二个参数为创建图片成功时的回调函数,成功时的回调函数第一个参数为图片的地址。第三个参数为失败时的回调函数,会返回失败原因以及状态码,第四个参数传this,是为了防止“canvasToTempFilePath: fail canvas is empty ”报错,报这个错是因为调取wx.canvasToTempFilePath 接口获取不到canvas,同样的需要传入上下文的this。

其中option的宽高,也是生成图片的宽高,我将其设置为元素的初始宽度的两倍,是因为如果是等比的话,图片会模糊。如果发现将宽高*2还是出现图片糊的情况,可以适当调整倍数。

//创建wxml2canvas对象
    let drawImage = new Wxml2Canvas({
      element: 'share', // canvas节点的id,
      obj: that, // 在组件中使用时,需要传入当前组件的this
      width: this.width * 2, // 宽高
      height: this.height * 2,
      background: '#fff', // 默认背景色
      progress(percent) { // 绘制进度
      },
      finish(url) {
        console.log("创建的图片", url);
      },
      error(res) {
        console.log(res);
        // uni.hideLoading()
        // 画失败的原因
      }
    }, this);

四、遇到的问题

文字绘制需要添加data-type=“text” data-text=“要绘制的文字”

<text class="time answer_draw_canvas" data-type="text" data-text="要绘制的文字">要绘制的文字</text>

边框不展示

要看一下是否设置了box-sizing: border-box;如果css设置了该属性会导致边框不出现

图片

图片本地的正常引用,如果是链接的一定要保证配置白名单,否则生成会一直加载中

文字加粗

官方文档是支持加粗的,但是我试过各种方法设置加粗都无济于事,估计是有bug。如果有看到这个文章的小伙伴调试出来了文字加粗麻烦留个言,在此,感谢!

边框圆角

目前未有边框圆角设置,所以ui有要求,要么改掉,或者加背景图片

背景色问题

如果背景色是设置在view或者其他官方未支持的标签时背景色是不会生效的,比如官方支持text标签,设置背景色是有效果的,这个要注意

五、总结

功能本身不复杂,逻辑也很简单,无非是怎么把页面结构转成canvas,通过 块级元素行内元素 的拆分可以将目标元素转为指定的 canvas ,从而可以让使用者利用小程序的 wx.canvasToTempFilePath 将canvas对象转为图片最终让用户进行保存。用什么工具去转最方便需要自己根据实际情况去判断。再说一点,微信小程序官方自带组件是很坑的,问题也不少,要注意,再会!

官方文档参考:https://github.com/wg-front/wxml2canvas


文章作者: feico
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 feico !
评论
  目录