Giới thiệu
Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách xây dựng một danh sách Masonry sử dụng Flexbox thông qua Next.js và React.js, đồng thời áp dụng SCSS Module để quản lý kiểu dáng hiệu quả. Đây là một giải pháp thích hợp cho việc hiển thị một lượng lớn nội dung một cách linh hoạt và đẹp mắt.
Giải pháp xây dựng danh sách
-
Chia mảng thành mảng đa chiều:
Chúng ta sẽ chia mảng dữ liệu thành các mảng con, tương ứng với số cột mà bạn mong muốn. -
Sử dụng Flexbox:
Chúng ta sử dụng Flexbox cho layout của danh sách, kết hợp với thuộc tính gap để tạo khoảng cách giữa các item. Kích thước width của mỗi item sẽ được tính toán theo công thức sau:scsscalc(100% / $column - $gap * (($column - 1) / $column))
-
Demo code:
Bạn có thể tham khảo đoạn mã mẫu tại đây.
Lưu ý khi sử dụng thư viện
Khi sử dụng thư viện để xây dựng danh sách Masonry, bạn có thể gặp phải hạn chế về chiều cao của các phần tử. Ví dụ, với số lượng lớn items như 9999, một số thư viện có thể gặp khó khăn trong việc load tất cả.
Sử dụng SCSS để tối ưu
Dưới đây là đoạn mã SCSS thiết lập kiểu dáng cho danh sách:
scss
.isScrolling {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: auto;
}
Hướng dẫn sử dụng mã nguồn
Dưới đây là mã nguồn chính để tạo danh sách Masonry:
javascript
'use client';
import { filter, map, slice } from 'lodash';
import { CSSProperties, Fragment, Suspense, memo, useEffect, useMemo, useState } from 'react';
import styles from './styles.module.scss';
export default memo(function Test() {
const [limit, setLimit] = useState(50);
const column = 5;
const list = map(Array.from({ length: 9999 }).fill(0), (_, idx) => {
return {
label: idx,
image: 'https://source.unsplash.com/random?t=' + idx,
};
});
const columnItems = Array.from({ length: column }, (_, colIndex) =>
filter(slice(list, 0, limit), (_, index) => index % column === colIndex)
);
const handleScroll = () => {
const boxList = document.getElementById('cover');
const listItem = document.getElementById('list');
if (boxList && listItem) {
if (
boxList.scrollTop + boxList.clientHeight >=
listItem.offsetHeight
) {
setLimit((prev) => prev + 150);
}
}
};
useEffect(() => {
window.addEventListener('wheel', handleScroll);
return () => {
window.removeEventListener('wheel', handleScroll);
};
}, [handleScroll]);
return (
<div className={styles.Container}>
<div className={styles.block}>
<div className={styles.isScrolling} id="cover">
<ul
id="list"
className={styles.list}
style={{ '--column': column } as CSSProperties}
>
{map(columnItems, (column, columnID) => {
return (
<Fragment key={columnID}>
<Suspense fallback={<p>Loading...</p>}>
<RenderColumn column={column} />
</Suspense>
</Fragment>
);
})}
</ul>
</div>
</div>
</div>
);
});
const RenderColumn = memo(({ column }) => {
return (
<li className={styles.column}>
{map(column, (row, rowID) => {
return (
<Fragment key={rowID}>
<Suspense fallback={<p>Loading...</p>}>
<RenderRow row={row} />
</Suspense>
</Fragment>
);
})}
</li>
);
});
const RenderRow = memo(({ row }) => {
return (
<div className={styles.row}>
<Suspense fallback={<p>Loading...</p>}>
{useMemo(() => <RenderItem item={row} />, [row])}
</Suspense>
</div>
);
});
const RenderItem = memo(({ item }) => {
return (
<div className={styles.card}>
<picture>
<img src={item.image} alt="" />
</picture>
<p>label: {item.label}</p>
</div>
);
});
SCSS cho giao diện
Dưới đây là mã SCSS cho giao diện của danh sách Masonry:
scss
.Container {
padding: 1rem;
display: flex;
flex: 1;
.block {
position: relative;
display: flex;
flex: 1;
margin: -1rem;
$gap: 1rem;
.list {
$column: var(--column);
padding: 1rem;
width: 100%;
list-style: none;
display: flex;
gap: $gap;
.column {
width: calc(100% / $column - $gap * (($column - 1) / $column));
display: flex;
flex-direction: column;
gap: $gap;
.row {
.card {
padding: 1rem;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.1);
border-radius: 0.5rem;
picture {
img {
width: 100%;
border-radius: 0.5rem;
object-fit: cover;
object-position: center;
}
}
}
}
}
}
}
}
Kết luận
Với hướng dẫn này, bạn đã có thể tạo ra một danh sách Masonry đa cột, dễ dàng quản lý và đẹp mắt sử dụng Flexbox và SCSS. Hãy thử áp dụng vào dự án của bạn và theo dõi kết quả nhé!
source: viblo