虚拟列表遇上瀑布流布局
虚拟列表遇上瀑布流布局
首先回忆一下瀑布流布局的基本思路:
- 根据返回的图片尺寸确定卡片的宽度,让当前的所有卡片根据该宽度先进行挂载
- 通过 nextTick 获取所有卡片 DOM 来确定高度
- 计算所有卡片位置进行瀑布流布局
而虚拟列表则是值渲染可视窗口内的dom元素,那么如何才能知道哪些元素在可视窗口里面呢?
在列表形式的虚拟列表中,我们只需要计算出可视数据的索引,然后直接截取出目标数据进行渲染即可,但是要知道瀑布流布局是个抽象的二维数组的结构,这时就不能简单的截取数据了,这样也没法实现,所以接下来就是解决计算哪些数据是可视的
计算可视数据
在实现瀑布流布局的时候,我们计算了每个item的大小和偏移量,这样在渲染的时候每个item都能取到它该区的位置,用到虚拟列表的话,就相当于我们只需要考虑可视窗口内的元素,那么窗口大小和滚动距离我们都很容易就能获取到,那么就能联想到对于每个item,我们只要计算出它的位置,然后判断它是否在可视窗口内就好了
// 计算要渲染的列表,过滤掉不可见的项
const renderList = computed(() =>
cardList.value.filter(i => i.h + i.y > scrollState.start && i.y < end.value)
)
这里很简单,只要判断该数据是否超出了可视窗口的上方和下方就好了
下面是每个item要用到的渲染数据
// 生成每个项目的渲染数据
const generatorItem = (item, before, index) => {
const rect = itemSizeInfo.value.get(item.id)
const width = rect.width
const height = rect.height
const imageHeight = rect.imageHeight
let y = 0
if (before) y = before.y + before.h + props.gap
return {
item,
y,
h: height,
imageHeight,
style: {
width: `${width}px`,
height: `${height}px`,
transform: `translate3d(${
index === 0 ? 0 : (width + props.gap) * index
}px, ${y}px, 0)`
}
}
}
以及最后渲染的目标队列
// 将新项目添加到队列中
const addInQueue = (size = props.enterSize) => {
for (let i = 0; i < size; i++) {
const minIndex = computedHeight.value.minIndex
const currentColumn = queueState.queue[minIndex]
const before = currentColumn.list[currentColumn.list.length - 1] || null
const dataItem = dataState.list[queueState.len]
const item = generatorItem(dataItem, before, minIndex)
currentColumn.list.push(item)
currentColumn.height = item.y
queueState.len++
}
}
完成了这些,就能配合虚拟列表完成瀑布流布局了