Material UI Image List 完全指南:从标准网格到 Masonry 布局与 ImageListItemBar 实战
本文围绕 Material UI(@mui/material)的 Image List 组件体系展开,覆盖官方文档中全部八种用法——standard、quilted、woven、masonry 四种变体,以及 ImageListItemBar 标题栏的各种位置定制。在给出可直接运行的完整示例的同时,结合仓库源码解析 CSS Grid 与 CSS 多列布局两种底层实现路径、默认值与插槽结构,帮助你彻底掌握图片画廊类页面(相册、商品墙、作品集)的搭建方案。
组件体系与源码位置
Image List 功能由三个组件协作完成,源码位于 packages/mui-material 包下:
| 组件 | 作用 | 源码位置 |
|---|---|---|
ImageList |
列表容器,负责列数、间距、布局变体 | ImageList.js |
ImageListItem |
单个列表项,决定跨越的行列数 | ImageListItem.js |
ImageListItemBar |
叠在图片上的标题栏,支持标题、副标题与操作图标 | ImageListItemBar.js |
ImageList 的核心 props 及默认值(由 ImageList.js 中的解构与 PropTypes 注释确认):
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
cols |
number | 2 |
列数 |
rowHeight |
number | 'auto' | 'auto' |
单行高度(px),标准/拼布变体中决定行高基准 |
gap |
number | 4 |
项与项之间的间距(px) |
variant |
'standard' | 'quilted' | 'woven' | 'masonry' | 'standard' |
布局变体 |
component |
element | 'ul' |
根节点,默认为无序列表 |
官方文档对应页面位于 image-list.md,所有可运行示例位于 docs/data/material/components/image-list 目录下。
标准图片列表(standard)
标准图片列表适合重要性相同的条目:容器尺寸、宽高比和间距完全一致。这是默认变体,只需设置 cols 与 rowHeight:
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
export default function StandardImageList() {
return (
<ImageList sx={{ width: 500, height: 450 }} cols={3} rowHeight={164}>
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
srcSet={`${item.img}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
src={`${item.img}?w=164&h=164&fit=crop&auto=format`}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
);
}
要点:
rowHeight={164}定义了单行基准高度,cols={3}时每格图片按 164×164 裁剪,因此src中请求的w=164&h=164与网格单元一一对应;srcSet提供dpr=2的 2 倍图,保证高分屏下清晰;loading="lazy"延迟加载,避免一次性请求全部图片。
完整示例可参考 StandardImageList.tsx。
拼布图片列表(quilted)
拼布列表通过让部分条目跨越多行多列来强调某些内容,形成视觉层级。启用方式是指定 variant="quilted",并给 ImageListItem 传入 cols / rows:
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
function srcset(image: string, size: number, rows = 1, cols = 1) {
return {
src: `${image}?w=${size * cols}&h=${size * rows}&fit=crop&auto=format`,
srcSet: `${image}?w=${size * cols}&h=${size * rows}&fit=crop&auto=format&dpr=2 2x`,
};
}
export default function QuiltedImageList() {
return (
<ImageList sx={{ width: 500, height: 450 }} variant="quilted" cols={4} rowHeight={121}>
{itemData.map((item) => (
<ImageListItem key={item.img} cols={item.cols || 1} rows={item.rows || 1}>
<img
{...srcset(item.img, 121, item.rows, item.cols)}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
);
}
// 数据示例:Breakfast 占 2 行 2 列,Burger 占 1 行 1 列……
const itemData = [
{ img: '…/photo-1551963831-b3b1ca40c98e', title: 'Breakfast', rows: 2, cols: 2 },
{ img: '…/photo-1551782450-a2132b4ba21d', title: 'Burger' },
{ img: '…/photo-1522770179533-24471fcdba45', title: 'Camera' },
{ img: '…/photo-1444418776041-9c7e33cc5a9c', title: 'Coffee', cols: 2 },
// ……其余 8 项
];
(完整 12 项数据见 QuiltedImageList.tsx)
从源码看,ImageListItem.js 中通过 gridColumnEnd: span ${cols} 与 gridRowEnd: span ${rows} 实现跨格,这正是 CSS Grid 的跨行跨列语法。而 srcset 辅助函数按 size * cols 与 size * rows 计算请求尺寸——跨越 2×2 的图片会请求 242×242 的裁剪图,保证大图不模糊。这是一个值得借鉴的技巧:图片请求尺寸必须等于实际渲染尺寸。
编织图片列表(woven)
编织列表使用交替的容器宽高比制造节奏感,适合浏览地位平等的内容。与标准变体相比,唯一区别是 variant="woven" 和 gap:
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
export default function WovenImageList() {
return (
<ImageList sx={{ width: 500, height: 450 }} variant="woven" cols={3} gap={8}>
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
srcSet={`${item.img}?w=161&fit=crop&auto=format&dpr=2 2x`}
src={`${item.img}?w=161&fit=crop&auto=format`}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
);
}
(完整示例见 WovenImageList.tsx)
woven 变体下奇数列的图片高度为 1 行、偶数列为 2 行,形成交错节奏;图片只需指定宽度(w=161),高度由网格行高自动裁剪。
瀑布流图片列表(masonry)
Masonry 变体使用动态的容器高度来反映每张图片的真实宽高比,适合浏览未经裁剪的原始内容:
import Box from '@mui/material/Box';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
export default function MasonryImageList() {
return (
<Box sx={{ width: 500, height: 450, overflowY: 'scroll' }}>
<ImageList variant="masonry" cols={3} gap={8}>
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
srcSet={`${item.img}?w=248&fit=crop&auto=format&dpr=2 2x`}
src={`${item.img}?w=248&fit=crop&auto=format`}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</Box>
);
}
(完整示例见 MasonryImageList.tsx)
从 ImageList.js 源码可以看到 masonry 与其他变体的本质区别:
- standard / quilted / woven:根节点是 CSS Grid,内联样式为
gridTemplateColumns: repeat(${cols}, 1fr)加上gap; - masonry:切换为
columnCount: cols, columnGap: gap的 CSS 多列布局,且根节点样式强制display: block。
这意味着 masonry 下 rowHeight 不起作用(列高由图片自然高度决定),同时项目按列方向排布、DOM 顺序自上而下流入各列,因此外层用 Box 包裹并设置 overflowY: 'scroll' 提供滚动容器。
带标题栏的列表(ImageListItemBar)
ImageListItemBar 为每个条目叠加一层信息栏,可容纳 title、subtitle 和一个次要操作 IconButton:
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar';
import ListSubheader from '@mui/material/ListSubheader';
import IconButton from '@mui/material/IconButton';
import InfoIcon from '@mui/icons-material/Info';
export default function TitlebarImageList() {
return (
<ImageList sx={{ width: 500, height: 450 }}>
<ImageListItem key="Subheader" cols={2}>
<ListSubheader component="div">December</ListSubheader>
</ImageListItem>
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
srcSet={`${item.img}?w=248&fit=crop&auto=format&dpr=2 2x`}
src={`${item.img}?w=248&fit=crop&auto=format`}
alt={item.title}
loading="lazy"
/>
<ImageListItemBar
title={item.title}
subtitle={item.author}
actionIcon={
<IconButton
sx={{ color: 'rgba(255, 255, 255, 0.54)' }}
aria-label={`info about ${item.title}`}
>
<InfoIcon />
</IconButton>
}
/>
</ImageListItem>
))}
</ImageList>
);
}
(完整示例见 TitlebarImageList.tsx)
ImageListItemBar 的关键 props(由 ImageListItemBar.js 中的 PropTypes 确认):
| Prop | 取值 | 默认值 | 说明 |
|---|---|---|---|
position |
'below' | 'bottom' | 'top' |
'bottom' |
默认 bottom 为图片底部渐变遮罩 |
actionPosition |
'left' | 'right' |
'right' |
操作图标的水平位置 |
title / subtitle |
node | — | 标题与副标题,支持字符串或元素 |
actionIcon |
node | — | 次要操作,通常是一个 IconButton |
注意示例中 ImageListItem 默认 component 就是 li,这里额外用了一个跨 2 列的条目承载 ListSubheader,说明 ImageList 中放置任意非图片内容是合法的。
标题栏置于图片下方(position="below")
通过 position="below" 可将标题栏放在图片之外,此时标题栏使用 position: relative(源码中 below 分支切换了定位方式),不再作为绝对定位遮罩:
<ImageList sx={{ width: 500, height: 450 }}>
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
srcSet={`${item.img}?w=248&fit=crop&auto=format&dpr=2 2x`}
src={`${item.img}?w=248&fit=crop&auto=format`}
alt={item.title}
loading="lazy"
/>
<ImageListItemBar
title={item.title}
subtitle={<span>by: {item.author}</span>}
position="below"
/>
</ImageListItem>
))}
</ImageList>
(完整示例见 TitlebarBelowImageList.tsx;masonry 版本见 TitlebarBelowMasonryImageList.tsx,用法相同,仅需把外层换成 masonry 变体的 ImageList 并加滚动容器。)
定制图片列表:渐变标题栏、置顶位置与 gap
最后一个官方示例综合展示了 gap prop、position="top"、actionPosition="left" 与自定义渐变 sx 背景:
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar';
import IconButton from '@mui/material/IconButton';
import StarBorderIcon from '@mui/icons-material/StarBorder';
function srcset(image: string, width: number, height: number, rows = 1, cols = 1) {
return {
src: `${image}?w=${width * cols}&h=${height * rows}&fit=crop&auto=format`,
srcSet: `${image}?w=${width * cols}&h=${height * rows}&fit=crop&auto=format&dpr=2 2x`,
};
}
export default function CustomImageList() {
return (
<ImageList
sx={{
width: 500,
height: 450,
// Promote the list into its own layer in Chrome. This costs memory, but helps keeping high FPS.
transform: 'translateZ(0)',
}}
rowHeight={200}
gap={1}
>
{itemData.map((item) => {
const cols = item.featured ? 2 : 1;
const rows = item.featured ? 2 : 1;
return (
<ImageListItem key={item.img} cols={cols} rows={rows}>
<img {...srcset(item.img, 250, 200, rows, cols)} alt={item.title} loading="lazy" />
<ImageListItemBar
sx={{
background:
'linear-gradient(to bottom, rgba(0,0,0,0.7) 0%, ' +
'rgba(0,0,0,0.3) 70%, rgba(0,0,0,0) 100%)',
}}
title={item.title}
position="top"
actionIcon={
<IconButton sx={{ color: 'white' }} aria-label={`star ${item.title}`}>
<StarBorderIcon />
</IconButton>
}
actionPosition="left"
/>
</ImageListItem>
);
})}
</ImageList>
);
}
(完整示例见 CustomImageList.tsx)
其中值得注意的三处技巧:
gap={1}:将默认 4px 的间距收窄到 1px,营造"照片墙"的紧凑感;transform: 'translateZ(0)':源码注释明确说明这是将列表提升为 Chrome 中的独立合成层,代价是内存,收益是滚动时保持高帧率——这是动画密集页面的常用手段;featured数据驱动布局:数据中的featured: true条目动态计算cols/rows为 2,实现"精选大图 + 常规小图"混排,无需切换整个列表的variant。
源码级原理补充
Grid 布局与 Context 传递
ImageList.js 将 rowHeight、gap、variant 打包为 Context 值下发:
const contextValue = React.useMemo(
() => ({ rowHeight, gap, variant }),
[rowHeight, gap, variant],
);
子组件 ImageListItem / ImageListItemBar 从 ImageListContext 读取这些值,据此计算自身尺寸与样式。这也解释了为什么 ImageListItem 的 rows/cols 必须以父级 rowHeight 为基准才能算出正确像素高度。
类名与覆盖机制
两个组件均遵循 MUI 的 utility class 约定(imageListClasses.ts / imageListItemClasses.ts),类名含 MuiImageList-root、MuiImageList-standard 等变体类,配合 classes prop 或主题 components.MuiImageList.styleOverrides 即可在不改动结构的前提下覆盖任意位置(例如只改 positionTop 类来换标题栏配色)。
实践建议小结
- 选择变体:条目均等用
standard;需要突出个别条目用quilted(或featured动态跨格);追求节奏感用woven;展示原始比例内容用masonry; - 图片尺寸:始终让
src/srcSet请求的尺寸匹配rowHeight与跨格数,并加dpr=2的 2 倍图与loading="lazy"; - 标题栏位置:遮罩式信息用默认
bottom;文字需要完整可读性时用position="below"(该模式下条目高度会包含标题栏,注意配合外层滚动容器); - 性能:长列表滚动时可用
translateZ(0)提升合成层。
以上示例与组件行为均基于当前仓库中 image-list.md 文档、docs/data/material/components/image-list 下的官方示例及 packages/mui-material/src/ImageList、ImageListItem、ImageListItemBar 目录的源码实现,可直接在任意 @mui/material 项目中复制使用。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00