写在前面

早上我查了很多瀑布流实现的方法,展示逻辑都不是很喜欢。

纸鹤给我看了一个使用了瀑布流布局的网页,网页利用transform对每个条目进行定位……恍然大悟。

中午做饭的时候看了B站一up主的瀑布流实现视频(麦克阿瑟还是有点实力的),大概知道怎样实现了。

瀑布流布局雏形

思路

用一个定长数组存放第n列某行条目的top值,每次便利找最小top值所在元素,添加一条新条目,并更新top值。等我去画个图。

image-20231211112404970

不完整的代码(仅提供一种思路)

使用react实现,和原生JS有些差别(是哟个原生JS实现可以直接看上面那个视频)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
export defualt testPage() {
// 创建一个布局容器
const boxRef = useRef(null);
// 存放条目列表
const [list, setList] = useState([])
// 判断列表图片是否加载完成
const [imagesLoaded, setImagesLoaded] = useState(0);

// 获取list
useEffect(() => {
getList(); // 该函数中应该确保获取到的列表图片全部加载完成
}, [])

// 判断图片是否全部加载完成
useEffect(() => {
imagesLoaded === list?.length && layout();
}, [imagesLoaded, list]);

// 绑定layout
useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
layout();
}
});

if (boxRef.current) {
resizeObserver.observe(boxRef.current);
}

return () => {
resizeObserver.disconnect();
};
}, [boxRef.current]);

// 布局函数
funciton layout() {
const box = boxRef.current;

// 计算容器参数
function getInfo() {
// 计算当前容器能存放多少列条目 cardWidth-每个条目的宽度
let boxWidth = box.offsetWidth;
let col = Math.floor(boxWidth / cardWidth);

// gap-条目间距
return {
boxWidth,
col,
gap, // 你可以定值或者计算gap值,推荐定值
}
}

// 获取top值最小的列
function getMinTop(nextTop) {
let min = nextTop[0],
minIdx = 0;
for (let i = 0, len = nextTop.length; i < len; i++) {
// ...
}

return {
min,
minIdx
}
}

// 获取top值最大的列
function getMaxTop() {}

let info = getInfo()
let nextTop = new Array(info.col).fill(0);

// 遍历每一个条目
for (let i = 0, len = box.children.length; i < len; i++ {
const card = box.children[i];
let minTop = getMinTop(nextTop);
const dirstX = 0; // 你可以设置一个初始值使得所有元素居中

// 卡片定位
card.style.transform = `translate(${
firstX + minTop.minIdx * (cardWidth + info.gap)
}px, ${
minTop.min + info.gap / 2
}px)`;

// 更新数组
nextTop[minTop.minIdx] = nextTop[minTop.minIdx] + card.offsetHeight + info.gap;
// 更新容器
let maxTop = getMaxTop(nextTop);
box.style.height = maxTop.max + 'px';
}
}

return (
<div>
<ul className="w-full relative">
{list.map(item => (
<div key={} className={absolute transition-all w-[]}>
<CardItem />
</div>
))}
</ul>
</div>
)
}

结合无限列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const [curPage, setCurPage] = useState(0);
const [totalPages, setTotalPages] = useState(1);
const containerRef = useRef(contianerRef)

function handleScroll() {
if (!contianerRef?.current) return;
const current = containerRef.current;

if (current.scrollTop === 0) {
//到顶
} else if (
current.scrollTop + current.clientHeight ===
current.scrollHeight
) {
// 触底
if (curPage >= totalPages) {
//没有更多
return;
} else {
setCurPage(curPage + 1);
}
}
}

// 页数改变时请求新一组分页数据
useEffect(() => {
if (curPage > totalPages) {
return;
}
getList();
}, [curPage]);

return (
<div ref={containerRef}
className='h-full w-full flex flex-col overflow-x-hidden overflow-y-auto'
onScroll={handleScroll}
>
<div>
{list && (
<ul>
{list.map(item) => (<div key={}>...</div>)}
</ul>
)}
</div>
</div>
)

实现中:抽成瀑布流无线列表组件

求交流各种妙的实现方法!