彻底解决瀑布流布局列高不均问题!3种方案让你的页面更美观

59 阅读4分钟

最近在写项目的时候运用到了瀑布流布局,瀑布流布局是因其美观的视觉效果,在图片展示类中非常常见,比如说小红书App、微信视频号...

但你是否也曾遇到过 列高不均 的问题?导致瀑布流布局的某一列越来越高,让人看得非常难受。。。所以今天我们就来彻底解决它!

一、问题现象:瀑布流为什么会列高不均?

首先来看一下项目中的瀑布流实现。

这是一个mockjs模拟 API 数据的配置文件:

//非完整代码
const getImages = (page,pageSize=10) => {
  return Array.from({length:pageSize}, (_, i) => ({
    id: `${page}-${i}`,
    height: Mock.Random.integer(400,600),
    url: Mock.Random.image('300x400', Mock.Random.color(), '#fff', 'img'),
  }))
};
...
 response:({query})=>{
   const page = Number(query.page) || 1;
   return {
    code: 0,
    data: getImages(page)
  }
}

将图片分为奇偶返回:

...
<div className={styles.column}>
  { images.filter((_, i) => i% 2 === 0 ).map(img => (
      <ImageCard url={img.url} height={img.height} key={img.id}/>
  ))}
</div>
<div className={styles.column}>
  { images.filter((_, i) => i% 2 !== 0 ).map(img => (
      <ImageCard url={img.url} height={img.height} key={img.id}/>
  ))}
</div>

这种实现方式非常的简单粗暴:

  1. 偶数 索引的图片放在第一列
  2. 奇数 索引的图片放在第二列
  3. 图片高度是随机生成的(400-600px)

这就导致了一个问题: 如果某一列连续出现多张高图片,这一列就会明显比另一列高 ,影响整体美观。

二、解决方案:3种方法让列高均匀

方案1:最小高度优先分配算法(推荐)

这是最常用也最有效的方法。核心思路是: 每次将新图片添加到当前高度最小的列。

实现步骤

  1. 维护一个数组,记录每列当前的总高度

  2. 每次添加新图片时,找到高度最小的列

  3. 将图片添加到这一列,并更新该列的总高度

代码改造

  // 重新分配图片到不同列
  useEffect(() => {
    if (images.length === 0) return;

    const newColumnHeights = [0, 0];
    const newSortedImages = [[], []];

    images.forEach(img => {
      // 找到高度最小的列
      const minColumnIndex = newColumnHeights[0] <= newColumnHeights[1] ? 0 : 1;

      // 添加到该列
      newSortedImages[minColumnIndex].push(img);
      // 更新该列高度
      newColumnHeights[minColumnIndex] += img.height;
    });

    setColumnHeights(newColumnHeights);
    setSortedImages(newSortedImages);
  }, [images]);

  // 监听滚动加载更多的逻辑不变
  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        fetchMore();
      }
    });
    if (loader.current) observer.observe(loader.current);
    return () => observer.disconnect();
  }, [fetchMore]);

  return (
    <div className={styles.wrapper}>
      <div className={styles.column}>
        {sortedImages[0].map(img => (
          <ImageCard url={img.url} height={img.height} key={img.id} />
        ))}
      </div>
      <div className={styles.column}>
        {sortedImages[1].map(img => (
          <ImageCard url={img.url} height={img.height} key={img.id} />
        ))}
      </div>
      <div ref={loader} className={styles.loader}>加载中...</div>
    </div>
  );
};

方案2:高度分组预分配

如果图片高度差异很大,可以尝试这种方法:

  1. 先将图片按高度排序
  2. 然后交替分配到不同列(高-低-高-低...)

这种方法可以确保每列都有高有低,避免某一列集中了太多高图片。

方案3:固定比例容器(简单但不灵活)

最简单的方法是给所有图片设置固定宽高比:

.card {
  width: 100%;
  padding-bottom: 150%; /* 固定宽高比为2:3 */
  position: relative;
}

.img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

但这种方法会导致图片被裁剪,适合对图片展示要求不高的场景。

三、为什么最小高度优先算法最有效?

  1. 动态平衡 :实时调整每列高度,确保始终保持平衡
  2. 适应性强 :无论图片高度如何变化,都能很好地适应
  3. 资源利用率高 :充分利用每一列的空间

四、优化与注意事项

  1. 性能优化 :
  • 如果图片数量很大,可以考虑使用虚拟列表
  • 避免在滚动时频繁重新计算列高
  1. 图片加载 :
  • 使用项目中已有的图片懒加载功能(IntersectionObserver
  • 确保图片加载完成后再更新列高
  1. 响应式布局 :
  • 在不同屏幕尺寸下,可以动态调整列数
  • 使用 CSS Grid 或 Flexbox 辅助布局
  1. 数据生成 :
  • 当前项目中图片高度是完全随机的(400-600px)
  • 可以考虑让高度有一定规律,避免极端情况
// 示例:生成更有规律的高度
height: 400 + (i % 5) * 50, // 400, 450, 500, 550, 600 循环

五、总结

瀑布流布局列高不均是一个常见问题,但通过最小高度优先分配算法可以很好地解决。这种方法实现简单,效果显著,是生产环境中最常用的解决方案。