什么是瀑布流布局
瀑布流布局,一般等宽,不等高的列表排列
原理是找出高度之和最小的那一列,在高度最小列继续添加元素
可以通过 absolute
定位实现,动态计算每一项的 top
和 left
封装瀑布流方法
function getAllRect(context, selector) {
return new Promise(function (resolve) {
wx.createSelectorQuery()
.in(context)
.selectAll(selector)
.boundingClientRect()
.exec(function (rect) {
if (rect === void 0) {
rect = []
}
return resolve(rect[0])
})
})
}
/**
* 瀑布流
* @param {*} context 页面或组件this对象
* @param {string} selector 选择器
* @param {Object} options
* @param {number=375} options.width 屏幕宽度
* @param {number=2} options.column 列数
* @param {number|string} options.gap 每列直接的间隙
* @param {number=0} options.padding 整个列表左右的padding
* @param {number=0}
* @returns {Array} 计算每项的top、left、height的数组
*/
async function waterFall(context, selector, options = {}) {
let items = await getAllRect(context, selector)
if (items.length <= 0) return []
let { gap = 15, column = 2, padding = 0, width = 375, firstColumnToTop = 0 } = options
// 1- 确定列数 = 页面的宽度 / 图片的宽度,单例的宽度
let itemWidth = items[0].width
// 定义每一列之间的间隙 px
if (gap === 'auto') {
gap = (width - itemWidth * column) / (column - 1)
}
let _columnHeightArr = [] // 保存每列高度
let result = []
for (let i = 0, len = items.length; i < len; i++) {
if (i < column) {
// 2- 确定第一行
let top = firstColumnToTop
let left = (itemWidth + gap) * i + padding
// 瀑布流列表左右padding
if (i === 0 || i === len - 1) {
left = padding
}
_columnHeightArr.push(items[i].height - top)
result.push({
top,
left,
height: items[i].height,
})
} else {
// 其他行
// 3- 找到数组中最小高度 和 它的索引
let minHeight = Math.min(..._columnHeightArr)
let minIndex = _columnHeightArr.findIndex((item) => item === minHeight)
// 4- 设置下一行的第一个盒子位置
// top值就是最小列的高度 + gap
result.push({
top: _columnHeightArr[minIndex] + gap,
left: result[minIndex].left,
height: items[i].height,
})
// 5- 修改最小列的高度
// 最小列的高度 = 当前自己的高度 + 拼接过来的高度 + 间隙的高度
_columnHeightArr[minIndex] = _columnHeightArr[minIndex] + items[i].height
}
}
return result
}
使用
vim demo.wxml
<!--需要子元素撑大父元素高度的情况,才需要设置height-->
<view class="goods_list flex flex-wrap relative" style="width:100%;height:{{height}}">
<block wx:for="{{list}}" wx:key="index">
<view class="goods_item" style="position:absolute;top:{{ item.top }}px;left:{{ item.left }}px"
bindtap="handleItem" data-item="{{item}}">
<view class="goods_img">
<goodsImage detail="{{ {imgUrl:item.goodsIcon} }}" isAllowCash="{{true}}">
</goodsImage>
</view>
<view class="goods_mes">
<view class="goods_name g-t-over2">{{item.goodsName}}</view>
<view class="goods_price">{{item.price}}</view>
</view>
</view>
</block>
</view>
vim demo.js
// 需要在节点加载到页面后调用
onReady() {
const { screenWidth, list } = this.data
waterFall(this, '.goods_item', { width: screenWidth, gap: 'auto' }).then((arr) => {
if (!arr.length) return
let lastNode = arr[arr.length - 1]
let height = lastNode.top + lastNode.height + 'px'
this.setData({
list: list.map((item, index) => ({ ...item, ...arr[index] })),
height,
})
})
}
效果
小结
既然都封装成函数了,为什么不封装成组件调用呢?
组件调用可以参考这个小程序的瀑布流组件me-waterfall
我看了这个组件的源码,用到组件间关系
来实现瀑布流结构,整个瀑布流组件内部也需要父子组件关系,父组件监听子组件插入元素,获取元素的动态高度,往高度最小列添加新元素。
但是我引入使用,在组件内使用me-waterfall
组件,组件间关系方法 linked
不生效,官方论坛也找不到原因,便弃用。
类似这样的结构:
<!--page.wxml-->
<view>
<goodsList />
</view
<!--goodsList.wxml-->
<view>
<me-waterfall>
<me-waterfall-item wx:for="{{list}}" wx:key="index">
<image src="{{item.imgUrl}}" style="width:100%;height:{{item.height}}px" />
</me-waterfall-item>
</me-waterfall>
</view
于是,自己封装一个方法使用。便有了此文。文章来源:https://www.toymoban.com/news/detail-657462.html
封装成方法也有优点,不需要引入组件,简单引入一下方法调用即可。文章来源地址https://www.toymoban.com/news/detail-657462.html
到了这里,关于小程序瀑布流实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!