E企盈营销工具技术服务商 热线:4006-838-530

小程序瀑布流组件

E企盈直播平台营销卖货系统

git仓库地址:https://github.com/kapeter/mpMasonry前段时间,接到一个需求,要在小程序中实现不定高度的瀑布流布局。我首先去万能的百度上搜索了一波,确实有很多方案,但都是固定高度的,这和需求不符。于是决定自己写一个,考虑到后面也会有类似的需求,干脆做成一个通用组件,方便使用。瀑布流是比较流行的一种网站页面布局,尤其在mobile端经常被用来展示信息流。前段时间,接到一个需求,要在小程序中实现不定高度的瀑布流布局。我首先去万能的百度上搜索了一波,确实有很多方案,但都是固定高度的,这和需求不符。于是决定自己写一个,考虑到后面也会有类似的需求,干脆做成一个通用组件,方便使用。本套方案主要使用flex模型,结合小程序的特性(boundingClientRect、抽象节点),实现瀑布流布局和组件化。组件布局由于手机宽度的限制,一般移动端的瀑布流只有两列,不需要考虑多列的情况,因此我们的布局完全可以通过CSS3的flex模型完成。!– masonry.wxml –view class=”masonry-list”    view class=”masonry-list-left” style=”{{ ‘margin-right:’ + intervalWidth }}”        view id=”left-col-inner”            block wx:for=”{{items}}” wx:key=”{{item.id}}”                masonry-item wx:if=”{{item.columnPosition === ‘left’}}” item=”{{item}}”/masonry-item            /block        /view    /view    view class=”masonry-list-right”        view id=”right-col-inner”            block wx:for=”{{items}}” wx:key=”{{item.id}}”                masonry-item wx:if=”{{item.columnPosition === ‘right’}}” item=”{{item}}”/masonry-item            /block        /view    /view/view// masonry.wxss.masonry-list {    width: 100%;    display: flex;    box-sizing: border-box;}.masonry-list-left, .masonry-list-right {    flex: 1;}flex为1,给予左右两列相同的宽度,通过设置properties中的intervalWidth来控制两者间的间距。有人可能会奇怪,为什么要在class=”masonry-list-left”的view之后再加一个层级?同一层级会怎么样呢?来看两个写法的对比图,我们统一给左边加上高度150px,右边高度设为auto。 很明显,在同一层级情况下,右边高度也变成了150px,与左边一致,这会导致我们后面获取两边高度的时候拿到一样的数值,就无法判断该把元素放在哪边。因此,我们要多增加一个层级。出现这种情况的原因是:flex的column会进行高度补全,和父容器保持一致。渲染函数基本布局已经完成,接下来就是要让布局“流”起来。先来看一下传统瀑布流的原理:先通过计算出一排能够容纳几列元素,然后寻找各列之中所有元素高度之和的最小者,并将新的元素添加到该列上,如此循环下去,直至所有元素均能够按要求排列为止。根据上述原理,渲染流程如下:/** * 渲染函数 *  * @param {Array} items  – 正在渲染的数组 * @param {Number} i  – 当前渲染元素的下标 * @param {Function} onComplete – 完成后的回调函数 */_render (items, i, onComplete) {    if (items.length i && !this.data.stopMasonry) {        this.columnNodes.boundingClientRect().exec(arr = {            const item = items[i]            const rects = arr[0]            const leftColHeight = rects[0].height            const rightColHeight = rects[1].height            this.setData({                items: […this.data.items, {                …item,                columnPosition: leftColHeight = rightColHeight ? ‘left’ : ‘right’                }]            }, () = {                this._render(items, ++i, onComplete)            })        })    } else {        onComplete && onComplete()    }}为了满足item高度是动态的场景,需要将渲染函数设置为递归函数。以下是渲染函数的执行流程:判断下标,如果递归结束,调用完成回调函数(onComplete),函数结束,反之执行下面流程;通过boundingClientRect()调取两边的高度;比较两边高度,将结果赋给columnPosition字段;调用setData()将新的元素渲染到dom上,新的元素位置基于columnPosition字段的值;渲染完成后,在setData()的回调函数中执行下一层递归,确保下一次boundingClientRect()能获取到最新的高度。setData()为异步渲染,详细说明请见小程序指南-双线程下的界面渲染由于每渲染一个元素,需要执行一次boundingClientRect()和setData(),渲染时间较长。exec()返回的是按请求次序构成的结果数组,即使只执行了一次请求,结果也位于res[0]而不是res。boundingClientRect的详细用法可查看小程序文档-WXML节点信息API。刷新函数有了核心的渲染函数,我们还要进行一些处理。/** * 刷新瀑布流 *  * @param {Array} items – 参与渲染的元素数组  */_refresh(items) {  const query = wx.createSelectorQuery().in(this)  this.columnNodes = query.selectAll(‘#left-col-inner, #right-col-inner’)  return new Promise((resolve, reject) = {    this._render(items, 0, () = {      resolve()    })  })}_refresh函数包括两部分:获取左右两列的WXML节点(这一步放在渲染函数中,会重复获取,影响性能)返回一个Promise对象,将_render函数包起来,并在_render的完成回调函数中触发resolve(),这样就能在渲染结束后执行其他操作。使用抽象节点剥离业务逻辑当前存在一个问题,masonry-item组件是用来承载元素的业务逻辑,如果项目存在多处需要瀑布流,并且业务逻辑不一样,那就需要修改masonry组件,添加判断条件,这就产生了耦合,不符合通用组件的规范。因此,我们需要进行解耦。这里需要用到“抽象节点”。以下是定义:自定义组件模版中的一些节点,其对应的自定义组件不是由自定义组件本身确定的,而是自定义组件的调用者确定的。这时可以把这个节点声明为“抽象节点”。简单来说,就是在masonry组件内部定义抽象节点masonry-item,这个节点可以代表任何组件,只有当页面调用masonry组件时,这个组件才被确定,这样就能将业务逻辑组件剥离出来了。具体实现很简单,在masonry组件声明抽象节点。// masonry.json”componentGenerics”: {    “masonry-item”: true}在页面调用时,指定该抽象节点为哪个组件。!– index.wxml –!– 指定抽象节点为img-box组件 –masonry generic:masonry-item=”img-box””/masonry注意点:节点的 generic 引用 generic:xxx=”yyy” 中,值 yyy 只能是静态值,不能包含数据绑定。因而抽象节点特性并不适用于动态决定节点名的场景。如何在页面中使用组件1、将components目录下中masonry文件夹复制到自己项目中。2、添加业务组件,并在业务组件中添加property,用于承载数据// property名必须为itemproperties: {    item: {         type: Object    }}3、引入masonry组件和所需的业务组件// index.json”usingComponents”: {    “masonry”: “../../components/masonry/masonry”,    “img-box”: “../../components/img-box/img-box”}4、在wxml加入masonry节点!– index.wxml –masonry generic:masonry-item=”img-box” id=”masonry” interval-/>原文https://www.kapeter.com/post/64

赞(0) 打赏
未经允许不得转载:E企盈小程序开发-热线:4006-838-530 » 小程序瀑布流组件
分享到: 更多 (0)
E企盈小程序直播营销卖货系统
E企盈直播平台营销卖货系统

评论 抢沙发

E企盈小程序开发

联系我们联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏