Skip to content

瀑布流(Waterfall)是一种常见的网页布局方式,尤其适用于图片或卡片的展示。这种布局的特点是内容块在页面上垂直堆叠,同时每个内容块的宽度可能不同,从而在视觉上形成类似瀑布的层叠效果。瀑布流布局可以提高页面的美感和用户体验。

react
import React, { useState, useEffect } from "react";

const Masonry = ({ columns = 3, gap = 4, images }) => {
  const [columnHeights, setColumnHeights] = useState(
    new Array(columns).fill(0)
  );
  const [items, setItems] = useState([]);

  useEffect(() => {
    const heights = new Array(columns).fill(0);
    const layoutItems = images.map((image) => {
      const columnIndex = heights.indexOf(Math.min(...heights));
      const newHeight = heights[columnIndex] + image.height + gap;
      heights[columnIndex] = newHeight;
      return {
        ...image,
        column: columnIndex,
      };
    });
    setItems(layoutItems);
    setColumnHeights(heights);
  }, [images, columns, gap]);

  return (
    <div className="flex gap-4">
      {Array.from({ length: columns }).map((_, columnIndex) => (
        <div key={columnIndex} className="flex flex-col gap-4 flex-1">
          {items
            .filter((item) => item.column === columnIndex)
            .map((item) => (
              <div key={item.src} className="mb-4">
                <img src={item.src} alt={item.alt} className="w-full block" />
              </div>
            ))}
        </div>
      ))}
    </div>
  );
};

export default Masonry;

这段代码实现了一个简单的 Masonry 布局组件,它根据列数 (columns)、间距 (gap)、以及传入的图片列表 (images) 来动态排列图片,使其形成一个瀑布流式的布局。

主要概念与逻辑

1. 状态管理

  • useState 钩子用于管理两个状态:

    • columnHeights:存储每一列的当前高度,初始值为 [0, 0, 0](假设默认列数为3)。
    • items:存储已经计算好布局的图片数据,包括图片属于哪一列的信息。
    javascript
    const [columnHeights, setColumnHeights] = useState(new Array(columns).fill(0));
    const [items, setItems] = useState([]);

2. 布局计算

  • useEffect 钩子在 images, columns, gap 改变时执行,用来计算每张图片应放在哪一列以及每一列的高度。

    javascript
    useEffect(() => {
      const heights = new Array(columns).fill(0);
      const layoutItems = images.map((image) => {
        const columnIndex = heights.indexOf(Math.min(...heights));
        const newHeight = heights[columnIndex] + image.height + gap;
        heights[columnIndex] = newHeight;
        return {
          ...image,
          column: columnIndex,
        };
      });
      setItems(layoutItems);
      setColumnHeights(heights);
    }, [images, columns, gap]);
    • heights:用于存储每一列的当前高度,初始值为 [0, 0, 0]
    • layoutItems:通过 map 遍历 images,为每张图片计算其所属的列 (columnIndex),根据当前最矮的列来决定图片放在哪一列。
    javascript
    const columnIndex = heights.indexOf(Math.min(...heights));
    • Math.min(...heights) 用来找到当前最矮的列,然后使用 heights.indexOf 找到对应的列索引 columnIndex
    • newHeight:计算图片放置后该列的高度,newHeight = heights[columnIndex] + image.height + gap;

    最后更新每列的高度,并返回新图片对象,包含图片本身的数据和它所属的列 (column)。

3. 布局渲染

  • return 语句中,根据计算出的 items 数组进行布局渲染。

    javascript
    return (
      <div className="flex gap-4">
        {Array.from({ length: columns }).map((_, columnIndex) => (
          <div key={columnIndex} className="flex flex-col gap-4 flex-1">
            {items
              .filter((item) => item.column === columnIndex)
              .map((item) => (
                <div key={item.src} className="mb-4">
                  <img src={item.src} alt={item.alt} className="w-full block" />
                </div>
              ))}
          </div>
        ))}
      </div>
    );
    • Array.from({ length: columns }):创建一个数组,其长度等于列数,以此来生成每一列的容器 <div>
    • filter:在每一列中,筛选出属于该列的图片。
    • map:遍历筛选后的图片,渲染每一张图片。
  • flex 布局:整个布局使用 flex 来创建水平排列的列,并在每一列中使用 flex-col 创建垂直排列的图片列表。

  • gap:用于设置图片之间的间距。

组件功能

  • 动态生成任意数量的列,并根据图片的高度和给定的间距实现瀑布流布局。
  • 图片被自动分配到当前最矮的列,以保持整体视觉平衡。
  • columnsgapimages 参数可动态调整,布局将自动更新。