Popular blog tags

背景
​目前小程序会有不少的应用场景里会用到无限长列表的交互,当一个页面展示很多信息的时候,会造成小程序页面的卡顿以及白屏。原因有如下几点:

列表数据很大,首次 setData 的时候耗时高
渲染出来的列表 DOM 结构多,每次 setData 都需要创建新的虚拟树、和旧树 diff 操作耗时都比较高
渲染出来的列表 DOM 结构多,占用的内存高,造成页面被系统回收的概率变大。
因此就有长列表组件来解决这些问题

实现思路
​核心的思路是只渲染显示在屏幕的数据,基本实现就是监听 scroll 事件,并且重新计算需要渲染的数据,不需要渲染的数据留一个空的 div 占位元素。

假设列表数据有100个 item,知道了滚动的位置,怎么知道哪些 item 必须显示在页面?因为 item 还没渲染出来,不能通过 getComputedStyle 等 DOM 操作得到每个 item 的位置,所以无法知道哪些 item 需要渲染。为了解决这个问题,需要每个 item 固定宽高。item 的宽高的定义见下面的 API 的createRecycleContext()的参数 itemSize 的介绍。

滚动过程中,重新渲染数据的同时,需要设置当前数据的前后的 div 占位元素高度,同时是指在同一个渲染周期内。页面渲染是通过 setData 触发的,列表数据和 div 占位高度在2个组件内进行 setData 的,为了把这2个 setData 放在同一个渲染周期,用了一个 hack 方法,所以定义 recycle-view 的 batch 属性固定为 batch="{{batchSetRecycleData}}"。

在滚动过程中,为了避免频繁出现白屏,会多渲染当前屏幕的前后2个屏幕的内容。

 

实现方法

 

step 1:下载源代码

直接通过 git 下载 wxa-plugin-canvas 源代码。

https://github.com/jasondu/wxa-plugin-canvas

step 2:将miniprogram_dist目录拷贝到自己的项目组件目录中

miniprogram\components\miniprogram_dist

 

首先设计界面

step 3:在 wxml 中使用组件

miniprogram/pages/my/UserPosterQrCodeService/index.wxml

首先放上poster的 id,用于显示后面将要生成的海报

<poster id="poster" config="{{posterConfig}}" bind:success="onPosterSuccess" bind:fail="onPosterFail">
   
</poster>

事件

<button bindtap="onCreatePoster">异步生成海报</button>

 

step 3:页面引入该组件

poster的 id需要引入组件,用来显示即将生成的海报

miniprogram/pages/my/UserPosterQrCodeService/index.json

{
  "usingComponents": {
    "poster": "/components/miniprogram_dist/poster/index"
  }
}

step 4:js页面引入该插件:

异步调用需要引入该插件

miniprogram/pages/my/UserPosterQrCodeService/index.js

import Poster from '../../../components/miniprogram_dist/poster/poster';

 

step 5:添加配置信息posterConfig

来源:https://github.com/jasondu/wxa-plugin-canvas/blob/a54cc04f4c638cbef33ecdf40de425afe68e1aa0/pages/index/index.js

(1)图片的域名添加到downloadFile合法域名中(开发设置-服务器域名-downloadFile合法域名)

    登陆微信公众平台https://mp.weixin.qq.com,输入小程序的登陆邮件和密码,需用管理员微信扫码完成登陆,否则会提示“没有配置权限”。

    选择开发-》开发设置-》开始配置,配置https域名

(2)在data中添加配置信息posterConfig

在生成海报前,需要准备一些用于海报的数据。

miniprogram/pages/my/UserPosterQrCodeService/index.js

 data: {
    url: '',
    winWidth: 0,
    winHeight: 0,
    oneHeight: 0, // 风景图的高度
    ewmWidth: 0, //二维码的高度

    oneWidth: 0, //左右两边的空白
    twoWidth: 0, //二维码和边框之间的距离
    fontHeight: 0,
    text1: '',
    text2: '',
    text3: '',
    text4: '',

    posterConfig: {
      width: 750,
      height: 1000,
      backgroundColor: '#fff',
      debug: false,
      blocks: [
        {
          x: 0,
          y: 10,
          width: 750, // 如果内部有文字,由文字宽度和内边距决定
          height: 120,
          paddingLeft: 0,
          paddingRight: 0,
          borderWidth: 10,
          borderColor: 'red',
          backgroundColor: 'blue',
          borderRadius: 40,
          text: {
            text: [
              {
                text: '金额¥ 1.00',
                fontSize: 80,
                color: 'yellow',
                opacity: 1,
                marginLeft: 50,
                marginRight: 10,
              },
              {
                text: '金额¥ 1.00',
                fontSize: 20,
                color: 'yellow',
                opacity: 1,
                marginLeft: 10,
                textDecoration: 'line-through',
              },
            ],
            baseLine: 'middle',
          },
        }
      ],
      texts: [
        {
          x: 0,
          y: 180,
          text: [
            {
              text: '长标题长标题长标题长标题长标题长标题长标题长标题长标题',
              fontSize: 40,
              color: 'red',
              opacity: 1,
              marginLeft: 0,
              marginRight: 10,
              width: 200,
              lineHeight: 40,
              lineNum: 2,
            },
            {
              text: '原价¥ 1.00',
              fontSize: 40,
              color: 'blue',
              opacity: 1,
              marginLeft: 10,
              textDecoration: 'line-through',
            },
          ],
          baseLine: 'middle',
        },
        {
          x: 10,
          y: 330,
          text: '金额¥ 1.00',
          fontSize: 80,
          color: 'blue',
          opacity: 1,
          baseLine: 'middle',
          textDecoration: 'line-through',
        },
      ],
      images: [
        {
          url: 'http://weixin.usdotnet.com/imgage/bgk_wanjiale.jpg',
          width: 300,
          height: 300,
          y: 450,
          x: 0,
          // borderRadius: 150,
          // borderWidth: 10,
          // borderColor: 'red',
        },
        {
          url: 'http://weixin.usdotnet.com/imgage/bgk_wanjiale.jpg',
          width: 100,
          height: 100,
          y: 450,
          x: 400,
          borderRadius: 100,
          borderWidth: 10,
        },
      ],
      lines: [
        {
          startY: 800,
          startX: 10,
          endX: 300,
          endY: 800,
          width: 5,
          color: 'red',
        }
      ]

    }

  },

 

step 6:添加生成事件处理函数

/**
   * 异步生成海报
   */
  onCreatePoster() {
    // setData配置数据
    this.setData({
      posterConfig: this.data.posterConfig
    }, () => {
      Poster.create();
    });
  },

点击生成按钮后海报生成成功的回调函数,可以得到转换后海报图像文件

/**
   * 生成海报成功-回调
   * @param {} e 
   */
  onPosterSuccess(e) {
    const {
      detail
    } = e;
    this.setData({
      //海报转换为图片的地址
      //http://tmp/wx9b00e80b6177fc26.o6zAJs-f-7jP3_rHnfQa5Vtxy2A8.dSrboGDj1uun236dd799633794e88a71f099d536c474.png
      posterImageUrl: detail,  
      isShowPosterModal: true
    })

点击生成按钮后海报生成失败的回调函数

 /**
   * 生成海报失败-回调
   * @param {*} err 
   */
  onPosterFail(err) {
    console.info(err)
  },

写一个wxml ,将图像显示出来,加上放大,保存等动作的处理。

 

wxa-plugin-canvas组件

组件原理说明文章

https://juejin.im/post/5b7e48566fb9a01a1059543f

使用案例

https://github.com/CavinCao/mini-blog

同类博客:

运用wxa-plugin-canva插件生成图片

https://blog.csdn.net/weixin_38710804/article/details/97927105

 

同类其他插件