// 通常配合上拉刷新一起使用(可见另一篇文章,交叉监听器的使用)
// 类似于PC抖音首页布局
//卡片宽度260px,左右间距6px,上下间距18px
废话不多说,直接上代码:
import React, { useState, useEffect, useRef } from 'react';
import './App.css';
function App() {
const [cards, setCards] = useState([]);
const containerRef = useRef(null);
useEffect(() => {
// 模拟异步加载卡片数据
const loadCards = async () => {
// 假设有一个 fetchCards 函数用于获取卡片数据
const data = await fetchCards();
setCards(data);
};
loadCards();
}, []);
useEffect(() => {
const masonryLayout = () => {
const container = containerRef.current;
const cards = container.getElementsByClassName('card');
const columnCount = Math.floor(container.offsetWidth / 266); // 每列宽度为 260px + 6px 左右间距
const columnHeights = Array(columnCount).fill(0);
for (let i = 0; i < cards.length; i++) {
const card = cards[i];
const minHeight = Math.min(...columnHeights);
const minHeightIndex = columnHeights.indexOf(minHeight);
const left = minHeightIndex * 266; // 每列宽度为 260px + 6px 左右间距
const top = minHeight;
card.style.left = `${left}px`;
card.style.top = `${top}px`;
columnHeights[minHeightIndex] += card.offsetHeight + 18; // 18px 为卡片之间的上下间距
}
const maxHeight = Math.max(...columnHeights);
container.style.height = `${maxHeight}px`;
};
masonryLayout();
window.addEventListener('resize', masonryLayout);
return () => {
window.removeEventListener('resize', masonryLayout);
};
}, [cards]);
return (
<div className="card-container" ref={containerRef}>
{cards.map((card, index) => (
<div key={index} className="card">
<img src={card.imageUrl} alt={card.title} />
<h3>{card.title}</h3>
<p>{card.description}</p>
</div>
))}
</div>
);
}
export default App;
.card-container {
position: relative;
}
.card {
position: absolute;
width: 260px;
background-color: #f2f2f2;
padding: 8px;
border-radius: 10px;
margin: 0
}
思路:
1、获取要展示的dom元素,根据dom去计算当前页面能展示几列数据
2、通过这个几列,将一个数组分成这个长度,数组的每一项代表每一列
3、根据这个数据展示开始的列表,每个卡片的高度不同(后端数据返回),展示开始的一行
4、再通过这个数组去查看那一列高度最低,完了吧第二行的第一个卡片放到这个最低的列,以此往复即可