You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Semantic Grid中按高度堆叠卡片(React.js+Semantic UI)

解决Semantic UI Grid卡片瀑布流布局问题

Semantic UI的Grid组件是基于行的布局,每一行的列会强制对齐高度,所以当卡片高度不一致时,就会出现你遇到的空隙问题。要实现「新卡片自动放入当前高度最短列」的瀑布流效果,需要换用其他布局方案,以下是几种可行的实现方式:


方法1:纯CSS多列布局(无需额外依赖)

利用CSS的column-count属性实现瀑布流,简单高效:

// 替换原有Grid组件,用容器div实现多列
<div className="ui container" style={{
  columnCount: 2, // 列数
  columnGap: '1.5rem', // 列间距
  padding: '1rem 0'
}}>
  {props.list.map((aProject) => (
    <div 
      key={aProject.id} 
      style={{
        breakInside: 'avoid', // 防止卡片被列拆分
        marginBottom: '1.5rem', // 卡片间距
        backgroundColor: '#fff',
        padding: '1rem',
        borderRadius: '4px',
        boxShadow: '0 1px 2px rgba(0,0,0,0.1)'
      }}
    >
      <ProjectCard project={aProject} />
    </div>
  ))}
</div>

这种方式的优点是无需引入第三方库,响应式可以通过媒体查询调整column-count实现,比如小屏幕下改为1列:

@media (max-width: 768px) {
  .card-container {
    column-count: 1;
  }
}

方法2:使用react-masonry-css库(更灵活的瀑布流)

如果需要更精细的控制(比如自定义响应式断点、动画效果),可以用轻量的瀑布流库react-masonry-css:

  1. 先安装依赖:
npm install react-masonry-css
  1. 组件中使用:
import Masonry from 'react-masonry-css';

// 定义响应式断点对应的列数
const breakpointColumns = {
  default: 2,
  768: 1 // 屏幕宽度≤768px时改为1列
};

// 配合基础样式
const styles = `
  .masonry-grid {
    display: flex;
    margin-left: -1.5rem;
    width: auto;
  }
  .masonry-grid-column {
    padding-left: 1.5rem;
    background-clip: padding-box;
  }
  .masonry-card {
    margin-bottom: 1.5rem;
  }
`;

// 渲染组件
<>
  <style>{styles}</style>
  <Masonry
    breakpointCols={breakpointColumns}
    className="masonry-grid"
    columnClassName="masonry-grid-column"
  >
    {props.list.map((aProject) => (
      <div key={aProject.id} className="masonry-card">
        <ProjectCard project={aProject} />
      </div>
    ))}
  </Masonry>
</>

方法3:手动计算列高分配卡片(无依赖但复杂)

如果不想用CSS多列或第三方库,可以手动维护两列的高度,将卡片分配到当前更短的列中:

import { useState, useEffect, useRef } from 'react';

function ProjectGrid({ list }) {
  const [column1, setColumn1] = useState([]);
  const [column2, setColumn2] = useState([]);
  const col1Ref = useRef(null);
  const col2Ref = useRef(null);

  useEffect(() => {
    // 初始化列数据
    const newCol1 = [];
    const newCol2 = [];
    let height1 = 0;
    let height2 = 0;

    list.forEach(project => {
      // 这里可以用预估高度,或者渲染后获取实际高度(需额外处理)
      const cardHeight = project.description.length * 18 + 120; // 示例预估高度
      
      if (height1 <= height2) {
        newCol1.push(project);
        height1 += cardHeight;
      } else {
        newCol2.push(project);
        height2 += cardHeight;
      }
    });

    setColumn1(newCol1);
    setColumn2(newCol2);
  }, [list]);

  return (
    <Grid stackable columns={2}>
      <Grid.Column ref={col1Ref}>
        {column1.map(p => <ProjectCard key={p.id} project={p} style={{ marginBottom: '1.5rem' }} />)}
      </Grid.Column>
      <Grid.Column ref={col2Ref}>
        {column2.map(p => <ProjectCard key={p.id} project={p} style={{ marginBottom: '1.5rem' }} />)}
      </Grid.Column>
    </Grid>
  );
}

注意:这种方法需要预估卡片高度,如果卡片高度是动态的(比如包含图片、可变长度文本),需要在渲染后通过ref获取实际高度再调整,实现起来比较繁琐,仅适合特殊场景。


内容的提问来源于stack exchange,提问作者Kristina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 22:48:51