Ant Design Masonry 组件完全指南:瀑布流布局 API、响应式列数与定位算法源码解析 📅 发布时间:2026/9/8 20:28:18 👁 浏览次数: Ant Design Masonry 组件完全指南瀑布流布局 API、响应式列数与定位算法源码解析【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-designMasonry 是 Ant Design 6.0.0 起在Layout分组中新增的瀑布流布局容器组件用于按“最短列优先”的规则把高度参差不齐的图片、卡片等内容均匀、紧凑地排列进多列网格。本文以 组件文档 为骨架结合组件源码Masonry.tsx、usePositions.ts与全部官方示例系统讲解其 API、响应式列数规则、内容测量/重排机制与语义化定制读完即可在真实项目中落地瀑布流场景。Masonry 是什么为“高度不齐”的内容而生瀑布流Masonry又称砖石布局与普通Row/Col栅格最大的区别在于每一列是**独立“堆叠”**的新条目总是被放进当前最矮的那一列从而让整体高度差最小、视觉上最紧凑。文档给出了三类典型的使用场景展示图片、卡片等高度不规则的内容如瀑布流相册需要内容在列方向上均匀分布避免某一列明显偏长需要列数随屏幕宽度自动响应。从组件目录看Masonry 主要由容器与子项两层实现组成并通过三个 hooks 协作完成测量与排布文件职责Masonry.tsx主容器断点计算、尺寸收集、位置计算、条目渲染编排MasonryItem.tsx单个条目含children/itemRender优先级与按需ResizeObserverusePositions.ts核心算法把“条目高度数组”排布为“列 top”坐标useRefs.ts以 key 为索引的子项 ref 管理useDelay.ts用requestAnimationFrame合并节流测量回调style/index.ts组件样式相对定位容器、透明度与位移动画组件通过 components/index.ts 以Masonry名义导出类型MasonryProps/MasonryRef一并导出入口见 masonry/index.tsx。快速上手固定列数的基础用法最直接的用法是通过items传入数据数组通过itemRender决定每个数据渲染成什么内容用columns固定列数、gutter设置间距。官方 basic 示例 完整代码如下import React from react; import { Card, Masonry } from antd; import type { MasonryProps } from antd; type MasonryItemType NonNullableMasonryPropsnumber[items][number]; const heights [150, 50, 90, 70, 110, 150, 130, 80, 50, 90, 100, 150, 60, 50, 80].map( (height, index) { const item: MasonryItemType { key: item-${index}, data: height, }; // 某一项可以直接塞 children优先级高于 itemRender if (index 4) { item.children ( Card sizesmall cover{img altfood src... /} Card.Meta titleIm Special descriptionLets have a meal / /Card ); } return item; }, ); const App: React.FC () ( Masonry columns{4} gutter{16} items{heights} itemRender{({ data, index }) ( Card sizesmall style{{ height: data }} {index 1} /Card )} / ); export default App;需要注意布局中每个条目虽然最终表现为绝对定位的格子但子项的真实高度由渲染后的 DOM 实测得到因此你完全可以只给每张卡片一个不同的固定高度如示例中 150/50/90… 的数字或者放一张width: 100%、高度自然变化的图片组件会自动收敛列高。API 一览组件通用属性说明可参考仓库内 Common Props 文档中文版。Masonry 主组件以下 API 表完整继承自 组件文档属性说明类型默认值版本全局配置classNames为组件内部各语义结构自定义类名支持对象或函数形式RecordSemanticDOM, string或(info: { props }) RecordSemanticDOM, string-6.0.06.0.0columns列数可为固定值或响应式配置number或{ xs?: number; sm?: number; md?: number }3-×fresh是否持续监听子项尺寸变化booleanfalse-×gutter间距可为固定值、响应式配置或“水平/垂直间距”配置Gap或[Gap, Gap]0-×items瀑布流条目数据MasonryItem[]--×itemRender自定义条目渲染函数(item: MasonryItem) React.ReactNode--×styles为组件内部各语义结构自定义内联样式支持对象或函数形式RecordSemanticDOM, CSSProperties或(info: { props }) RecordSemanticDOM, CSSProperties-6.0.06.0.0onLayoutChange条目列归属发生变化时的回调({ key: React.Key; column: number }[]) void--×MasonryItemitems数组中的单个对象属性说明类型默认值children自定义展示内容优先于itemRenderReact.ReactNode-column指定条目归属的列number-data自定义数据存储会作为itemRender回调参数传入T-height条目高度number-key条目的唯一标识string或number-需要说明Masonry的类型是泛型的MasonryPropsItemDataType。给每个数据指定key是必须的源码中key: React.Key为必填它同时作为 DOM 测量的索引与动画 diff 的标识data则用于承载业务数据。Gap 类型Gap表示条目间的间距可以是一个固定值也可以是响应式配置type Gap undefined | number | PartialRecordxs | sm | md | lg | xl | xxl, number;若把gutter写成[水平间距, 垂直间距]的二元组则可以分别控制列间距与行间距。columns固定列数与响应式断点匹配规则columns不传时默认为3源码 Masonry.tsx 中if (!columns) return 3传入纯数字则直接使用。当传入响应式对象时组件借助useBreakpoint()实现见 useBreakpoint.tsx底层媒体查询由 _util/responsiveObserver.ts 维护收集当前命中的屏幕断点随后按从大到小的顺序xxxl → xxl → xl → lg → md → sm → xs查找第一个“当前已命中且在columns中显式配置了列数”的断点一个都没命中时回退到columns.xs ?? 1。该匹配规则决定了书写习惯给大屏配置的断点要在小屏配置之前被“命中”因此在对象中只需写出关键档位即可。官方 responsive 示例const heights [120, 55, 85, 160, 95, 140, 75, 110, 65, 130, 90, 145, 55, 100, 80]; const App: React.FC () { const items heights.map((height, index) ({ key: item-${index}, data: height, index, })); return ( Masonry columns{{ xs: 1, sm: 2, md: 3, lg: 4 }} gutter{{ xs: 8, sm: 12, md: 16 }} items{items} itemRender{(item) ( Card sizesmall style{{ height: item.data }} {item.index 1} /Card )} / ); };该示例中columns与gutter都使用响应式对象随着窗口从手机宽度放大到桌面宽度瀑布流会依次从单列切换为 2、3、4 列间距也随之增大。由于断点来自useBreakpoint订阅实现见 responsiveObserver.ts窗口变化时组件会自动重算并触发条目重新落位无需手动刷新。定位样式与总高度的计算容器内部每个条目都是绝对定位的。源码中每个条目的位置与尺寸由一组计算表达式得出Masonry.tsxconst itemStyle: CSSProperties { [--ant-masonry-item-width]: calc((100% ${horizontalGutter}px) / ${columnCount}), insetInlineStart: calc(var(--ant-masonry-item-width) * ${columnIndex}), width: calc(var(--ant-masonry-item-width) - ${horizontalGutter}px), top: position.top, position: absolute, };也就是说每条宽度 “容器总宽 间距÷ 列数 − 间距”水平起点 该列下标 × 单列宽度含间距垂直起点top则由定位算法给出容器的height会由算法直接写成“各列中最高的那一列的总高度”见下节usePositions返回值totalHeight从而撑起外层布局。另外在 RTL 环境下会自动追加${prefixCls}-rtl样式类Masonry.tsx。gutter固定值、响应式与双向间距gutter共有三种写法官方文档全部支持固定数值如gutter{16}表示行列间距均为 16px二元组如gutter{[16, 24]}分别表示水平列间距与垂直行间距响应式对象如gutter{{ xs: 8, sm: 12, md: 16 }}。源码中 gutter 与 antd 栅格共用同一套解析逻辑useGutter(gutter, screens)先解析出水平/垂直两组值再按[horizontalGutter 0, verticalGutter horizontalGutter]展开Masonry.tsx因此只给单个值时垂直间距会复用水平间距缺省整体为0。verticalGutter会被传入usePositions参与列高的累加每放一个条目列高增加height verticalGutter以保证行间距真实生效。内容渲染children 优先itemRender 兜底每个条目到底渲染成什么由 MasonryItem.tsx 决定const renderNode useMemo(() { return item.children ?? itemRender?.({ ...item, index, column }); }, [item, itemRender, column, index]);即item.children优先适合个别条目的特殊展示例如上面的“Special”卡片只有未提供children时才调用itemRender。itemRender收到的回调参数是展开后的完整 item包含key、data、column、children等并额外附带该条目的index数据下标与column当前所在列因此你可以同时拿到业务数据和位置信息。这与文档 API 表里itemRender的类型(item: MasonryItem) React.ReactNode一致更完整的 TS 定义为MasonryItem { index: number }见 Masonry.tsx。item.column 钉列与 onLayoutChange动态增删的“受控回流”瀑布流默认是“自动落位”的新条目塞进当前最矮的列。但有些场景如图片墙里删除某一张你希望条目保持相对顺序稳定、不至于整体乱跳。官方 dynamic 示例 演示了完整闭环Masonry columns{4} gutter{16} items{items} itemRender{({ data, key }) ( Card sizesmall style{{ height: data }} {Number(key) 1} Button style{{ position: absolute, insetBlockStart: token.paddingSM, insetInlineEnd: token.paddingSM }} sizesmall icon{CloseOutlined /} onClick{() removeItem(key)} / /Card )} onLayoutChange{(sortedItems) { setItems((prevItems) prevItems.map((item) { const matchItem sortedItems.find((sortedItem) sortedItem.key item.key); return matchItem ? { ...item, column: matchItem.column } : item; }), ); }} /点击右上角关闭按钮删除任意条目点击底部按钮追加一条随机高度数据。这里的关键机制是写入钉列初始数据里可为每个 item 附带column示例中为index % 4把条目“钉”到指定列回调回写当布局因增删而重新排布后onLayoutChange会把每个条目的最新key → column关系回传给业务方业务方据此更新自身 state 中的column字段再回流给组件从而让其余条目尽量停在原来的列里只“补位”而非“全员乱序”。这个“稳定优先”的设计可以从 usePositions.ts 的注释中读到原作者的意图Always get stable positions by order instead of dynamic adjust for next item height.始终按顺序获得稳定位置而不是针对下一个条目的高度做动态调整。定位算法选“当前最矮的列”usePositions.ts 用一个很精简的“贪心”算法完成排布const columnHeights new Array(columnCount).fill(0) as number[]; for (let i 0; i itemHeights.length; i 1) { const [itemKey, itemHeight, itemColumn] itemHeights[i]; let targetColumnIndex itemColumn ?? columnHeights.indexOf(Math.min(...columnHeights)); targetColumnIndex Math.min(targetColumnIndex, columnCount - 1); const top columnHeights[targetColumnIndex]; itemPositions.set(itemKey, { column: targetColumnIndex, top }); columnHeights[targetColumnIndex] itemHeight verticalGutter; }要点有三初始化一个长度等于列数的“列高数组”全部为 0每个条目若自身带column则钉到该列并对列下标做Math.min(columnCount - 1)越界保护否则落在当前列高最小的那一列indexOf(Math.min(...))保证多列等高时取最左侧列行为确定落位后把该列高度加上“条目高 垂直间距”直到所有条目处理完容器总高度取各列高度的最大值再减去末尾多余的一个垂直间距。条目高度数据则来自真实 DOMcollectItemSize遍历每个 ref用getBoundingClientRect()实测渲染高度Masonry.tsx这解释了为什么示例里只要给卡片height样式即可而不必手动上报高度。fresh持续监听尺寸变化及其代价默认情况下Masonry 只在“条目集合或列数变化”“容器尺寸变化”“子项图片的 load/error 事件”等时机触发一次重测容器外层包裹了ResizeObserver并注册了onLoad/onError事件见 Masonry.tsx因此资源开销较小。但如果你的子项高度会动态变化例如点击卡片后高度发生过渡动画就需要开启fresh——此时组件会给每个条目额外套一个ResizeObserver持续监听MasonryItem.tsx 中onResize ? ResizeObserver onResize{onResize}…并在条目尺寸每次变化后都重新收集高度与落位。官方 fresh 示例 中的卡片可点击随机改变自身高度Masonry fresh columns{4} gutter{16} items{heights} itemRender{({ data, index }) RandomHeightCard index{index} defaultHeight{data} /} /Card sizesmall style{{ height, transition: height 0.3s }} onClick{() setHeight(...)}文档示例的中文注释给出了明确提醒“通过fresh持续监听尺寸变化会有性能损耗”见 fresh.md。因此fresh默认值为false仅当子项高度确实会运行时变化动画、折叠、懒加载等时才需要开启反之条目较多时应尽量保持关闭或把高度变化收敛为条目自身的过渡动画避免每个条目的监听器高频触发全量重排。测量为何“延迟”即使触发了重测也并非同步执行collectItemSize通过 useDelay.ts 的raf(callback)包裹同一帧内多次触发只会合并执行最后一次避免布局抖动期间产生中间态测量且只有当新测量结果与上一次不同isEqual比较时才会触发 state 更新减少无谓重渲染。变更动效出现 / 离场 / 位移条目发生移动、新增、删除时Masonry 会呈现平滑过渡。实现分为两层动效编排条目渲染在rc-component/motion的CSSMotionList内Masonry.tsx开启motionAppear与motionLeave动效名称为${prefixCls}-item-fade样式定义style/index.ts 中定义了入场/离场透明度动画opacity过渡以及对“非动效态”条目即位移中的常规条目的left/right/top三个方向的位移过渡时长取自全局motionDurationSlow/motionDurationFast。因此批量增删条目、或者窗口断点切换导致大量条目换列时界面会看到淡入淡出与平滑滑动而不是瞬间跳动。容器本身则使用相对定位 flex 列换行模型style/index.ts其中 flex 主要服务于文档流语义条目的实际网格坐标仍由内联绝对定位样式驱动。classNames / styles按语义结构精准定制与 antd 5.x/6.x 其他组件一致Masonry 暴露了root与item两个语义化 DOM 节点演示见 _semantic.tsxroot根容器元素承担相对定位、flex 布局与瀑布流容器样式item单个条目元素承担绝对定位、宽度计算、过渡动画与瀑布流项目样式。classNames/styles支持对象与函数两种写法。函数形式会收到{ props }其中props.columns已被解析为当前生效的具体列数见 Masonry.tsx 的mergedProps因此可以依据运行时状态做条件定制。官方 style-class 示例 给出了完整的对象与函数对照// 函数形式根据当前列数动态决定边框颜色 const stylesFn: MasonryProps[styles] (info) { const { props } info; return { root: { border: 2px solid ${ typeof props.columns number props.columns 2 ? #1890ff : #52c41a }, padding: 20, height: 280, backgroundColor: rgba(240,248,255,.6), }, item: { boxShadow: 0 2px 8px rgba(0,0,0,0.1), border: 1px solid #1890ff, }, }; };想定制类名与内联样式的优先级关系context → prop → root style与合并顺序可参考该组件的 semantic 测试其中验证了classNames/styles的对象键与函数签名类型以及语义样式优先级classNames/styles两个属性自6.0.0起可用且支持通过ConfigProvider的组件级全局配置Global Config同版本 6.0.0统一注入。图片瀑布流实战懒加载友好写法对“图片高度未知”的典型场景官方 image 示例 给出了一种零手工测高写法——图片直接以width: 100%渲染、高度由图片自身纵横比自然撑开再由组件自动测量const App () ( Masonry columns{4} gutter{16} items{imageList.map((img, index) ({ key: item-${index}, data: img, }))} itemRender{({ data }) ( img src{${data}?w523autoformat} altsample style{{ width: 100% }} / )} / );由于容器监听了子项图片资源的onLoad/onError即使图片加载有快有慢每当有图片加载完成都会触发一次重测并把后续条目平滑下移最终仍能收敛成紧凑的瀑布流无需为每张图片预写占位高度。Design Token 与主题定制说明组件文档中以ComponentTokenTable componentMasonry /挂载了该组件的 Token 表。从 style/index.ts 的实现看Masonry的ComponentToken当前为空接口export interface ComponentToken {}即没有定义组件专属的样式 Token其样式主要复用 antd 主题的通用运动 TokenmotionDurationSlow、motionDurationFast、motionEaseOut来控制淡入淡出与位移过渡的节奏。因此若想调整动画快慢可以通过主题 Tokenmotion 相关全局 Token整体生效若想精细定制列宽、圆角、边框、悬浮态等外观更推荐直接使用上文介绍的classNames/styles针对 root/item 两个语义节点或对.ant-masonry/.ant-masonry-item类名书写覆盖样式源码中的前缀类名与genStyleHooks(Masonry, …)接入方式一致。小结把官方文档、官方示例与源码放在一起看Masonry 的使用模型其实非常清晰数据驱动items带key与data的数组itemRender/children决定“渲染什么”高度一律交给真实 DOM 测量两套排布模式不给column时自动选择“当前最矮列”见 usePositions.ts给了column并配合onLayoutChange回写后可保持相对顺序稳定适合图片墙类的增删场景三种粒度控制columns/gutter支持固定值与响应式断点大断点优先匹配fresh决定是否对子项尺寸做持续监听有性能代价classNames/styles支持按语义节点做对象/函数式样式定制动画与测量自动收敛重测经由 rAF 节流出现/离场/位移均有内置过渡图片 load/error 也会自动触发重排。对于需要在 Ant Design 项目中实现相册瀑布流、高度不一的卡片墙或响应式内容墙的开发者Masonry 是开箱即用的选择若想进一步深挖推荐继续阅读 组件源码、定位算法、组件测试 以及其余 官方示例。【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考