
Material UI Avatar 与 AvatarGroup 完全指南图片、文字、图标头像及组合堆叠的实战实现【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Googles Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-uiMaterial UIMUI的Avatar组件是 Material Design 体系中用于展示用户、联系人或文件负责人的基础视觉单元在表格、对话框菜单、消息列表等几乎所有界面中都会出现。本文基于 MUI 官方文档 avatars.md 的全部内容结合 packages/mui-material/src/Avatar/Avatar.js 与 packages/mui-material/src/AvatarGroup/AvatarGroup.js 的真实源码实现系统讲解头像的四种创建形态图片、文字、图标、变体、尺寸控制、图片加载失败的回退机制、AvatarGroup的堆叠组合max/total/renderSurplus/spacing、与Badge的状态角标组合以及头像上传交互的完整实现。读完本文你可以独立完成头像组件的全部配置并理解每个 prop 在源码层面的实际行为。创建头像的四种基本形态Avatar组件的形态完全由传入的 props 决定传src/srcSet得到图片头像传字符串children得到文字头像传图标元素children得到图标头像。从源码 Avatar.js 的渲染逻辑可以清楚看到这一优先级hasImgNotFailing图片加载成功→children→alt首字母 → 通用Person图标。图片头像Image avatars图片头像通过传入标准img的src或srcSet属性创建对应的官方示例 ImageAvatars.tsximport Avatar from mui/material/Avatar; import Stack from mui/material/Stack; export default function ImageAvatars() { return ( Stack directionrow spacing{2} Avatar altRemy Sharp src/static/images/avatar/1.jpg / Avatar altTravis Howard src/static/images/avatar/2.jpg / Avatar altCindy Baker src/static/images/avatar/3.jpg / /Stack ); }其中alt属性用于为渲染出的img元素提供替代文本同时承担无障碍描述职责srcSet用于响应式图片显示还会透传sizes属性。在 Avatar.js 中这些属性都通过additionalProps: { alt, src, srcSet, sizes }交给imgslot。文字头像Letter avatars通过传入字符串作为children创建简单字符头像并可用sx的bgcolor自定义背景色示例 LetterAvatars.tsximport Avatar from mui/material/Avatar; import Stack from mui/material/Stack; import { deepOrange, deepPurple } from mui/material/colors; export default function LetterAvatars() { return ( Stack directionrow spacing{2} AvatarH/Avatar Avatar sx{{ bgcolor: deepOrange[500] }}N/Avatar Avatar sx{{ bgcolor: deepPurple[500] }}OP/Avatar /Stack ); }一个更实用的模式是根据用户名自动生成背景色。官方示例 BackgroundLetterAvatars.tsx 用字符串哈希算法将姓名映射为稳定的十六进制颜色function stringToColor(string: string) { let hash 0; let i; for (i 0; i string.length; i 1) { hash string.charCodeAt(i) ((hash 5) - hash); } let color #; for (i 0; i 3; i 1) { const value (hash (i * 8)) 0xff; color 00${value.toString(16)}.slice(-2); } return color; } function stringAvatar(name: string) { return { sx: { bgcolor: stringToColor(name) }, children: ${name.split( )[0][0]}${name.split( )[1][0]}, }; } Avatar {...stringAvatar(Kent Dodds)} /注意Avatar默认背景色并非灰色透明而是由colorDefault类控制。在 Avatar.js 中当没有成功加载的图片时组件会标记ownerState.colorDefault true此时背景色在 CSS 变量模式下取自theme.vars.palette.Avatar.defaultBg否则为theme.palette.grey[400]暗色模式下自动切换为grey[600]。图标头像Icon avatars将图标组件作为children传入即可创建图标头像示例 IconAvatars.tsximport { green, pink } from mui/material/colors; import Avatar from mui/material/Avatar; import Stack from mui/material/Stack; import FolderIcon from mui/icons-material/Folder; import PageviewIcon from mui/icons-material/Pageview; import AssignmentIcon from mui/icons-material/Assignment; Stack directionrow spacing{2} AvatarFolderIcon //Avatar Avatar sx{{ bgcolor: pink[500] }}PageviewIcon //Avatar Avatar sx{{ bgcolor: green[500] }}AssignmentIcon //Avatar /Stack形状变体Variants需要方形或圆角头像时使用variantprop取值circular默认、rounded、square示例 VariantAvatars.tsxAvatar sx{{ bgcolor: deepOrange[500] }} variantsquareN/Avatar Avatar sx{{ bgcolor: green[500] }} variantrounded AssignmentIcon / /Avatar从源码 Avatar.js 的variants配置看三种变体对应的borderRadius分别是circular使用根样式中的50%rounded使用主题变量theme.shape.borderRadius默认 4px可随主题定制square直接为0。每个变体还对应独立的工具类见 avatarClasses.ts 中导出的circular/rounded/squareclass key可通过classesprop 精细覆盖。尺寸控制Sizes头像的默认尺寸是40×40来自根样式的width: 40; height: 40Avatar.js。文档指出可以通过height和widthCSS 属性改变大小推荐写法是用sxprop示例 SizeAvatars.tsxStack directionrow spacing{2} Avatar altRemy Sharp src/static/images/avatar/1.jpg sx{{ width: 24, height: 24 }} / Avatar altRemy Sharp src/static/images/avatar/1.jpg / Avatar altRemy Sharp src/static/images/avatar/1.jpg sx{{ width: 56, height: 56 }} / /Stack由于内部img元素的样式是width: 100%; height: 100%; objectFit: coverAvatar.js调整根容器宽高时图片会等比裁剪填充无需额外处理非正方形素材。图片加载失败的回退机制Fallbacks这是Avatar最重要的健壮性特性当头像图片加载出错时组件按以下固定顺序回退到替代内容对应文档原文提供的childrenalt文本的首字母通用头像图标内置PersonSVG。官方示例 FallbackAvatars.tsx 用三个破损的src演示了这三种回退Stack directionrow spacing{2} Avatar sx{{ bgcolor: deepOrange[500] }} altRemy Sharp src/broken-image.jpgB/Avatar Avatar sx{{ bgcolor: deepOrange[500] }} altRemy Sharp src/broken-image.jpg / Avatar src/broken-image.jpg / /Stack三个头像分别回退为显式传入的字符B、alt首字母R、内置Person图标。实现原理判断图片是否加载成功并不依赖img元素的onError事件而是 Avatar.js 中的useLoadedhook——它在useEffect中手动new Image()预加载src/srcSet监听onload/onerror返回loaded或error状态。源码注释明确说明了原因Use a hook instead of onError on the img element to support server-side rendering在 SSR 场景下img的onError不可靠而独立Image对象可以在浏览器端稳定工作。最终渲染分支Avatar.jsif (hasImgNotFailing) { children ImgSlot {...imgSlotProps} /; } else if (!!childrenProp || childrenProp 0) { children childrenProp; } else if (hasImg alt) { children alt[0]; } else { children FallbackSlot {...fallbackSlotProps} /; }另外img样式中还有两处细节color: transparent隐藏 alt 文本textIndent: 10000隐藏 Chrome 的破损图片图标。AvatarGroup 头像组GroupedAvatarGroup将子级Avatar渲染为相互堆叠的一排用maxprop 限制显示数量超出部分显示为n角标。基础用法示例 GroupAvatars.tsximport Avatar from mui/material/Avatar; import AvatarGroup from mui/material/AvatarGroup; AvatarGroup max{4} Avatar altRemy Sharp src/static/images/avatar/1.jpg / Avatar altTravis Howard src/static/images/avatar/2.jpg / Avatar altCindy Baker src/static/images/avatar/3.jpg / Avatar altAgnes Walker src/static/images/avatar/4.jpg / Avatar altTrevor Henderson src/static/images/avatar/5.jpg / /AvatarGroupmax的默认值是5AvatarGroup.js。实现上有几个值得注意的源码细节max最小为 2const clampedMax max 2 ? 2 : max;传小于 2 的值会被钳制为 2且 PropTypes 会给出警告 The propmaxshould be equal to 2 or above堆叠方向根元素使用flexDirection: row-reverse并渲染前maxAvatars个子级AvatarGroup.js每个子级自动加上avatar类带 2px 主题背景色边框border: 2px solid theme.palette.background.default以制造分层边缘效果不接受 Fragment源码在开发模式下会对React.Fragment子级打印错误建议使用数组代替variant 透传AvatarGroup自身也有variantprop默认circular通过React.cloneElement注入每个子级除非子级自己显式设置了variant。控制总数量total propmax只影响渲染多少个子头像而total用于控制未显示头像的总数即n角标上的数字。示例 TotalAvatars.tsxAvatarGroup total{24} Avatar altRemy Sharp src/static/images/avatar/1.jpg / Avatar altTravis Howard src/static/images/avatar/2.jpg / Avatar altAgnes Walker src/static/images/avatar/4.jpg / Avatar altTrevor Henderson src/static/images/avatar/5.jpg / /AvatarGroup这里只渲染 4 个子头像但角标显示20。不传total时默认取children.length源码const totalAvatars total || children.length;。自定义角标renderSurplus将renderSurplus设置为回调函数即可自定义n角标的内容。回调接收一个参数——基于children与maxprop 计算出的超出数量surplus number返回React.ReactNode。当需要根据服务端数据渲染超出数量时尤其有用。示例 CustomSurplusAvatars.tsxAvatarGroup renderSurplus{(surplus) span{surplus.toString()[0]}k/span} total{4251} Avatar altRemy Sharp src/static/images/avatar/1.jpg / {/* ... */} /AvatarGroup这里total{4251}、max默认 5回调收到4247取首位数字渲染为4k。从源码 AvatarGroup.js 看超出数量的完整计算链为const totalAvatars total || children.length; if (totalAvatars clampedMax) { clampedMax 1; } // 恰好相等时多显示一位避免出现0 clampedMax Math.min(totalAvatars 1, clampedMax); const maxAvatars Math.min(children.length, clampedMax - 1); const extraAvatars Math.max(totalAvatars - clampedMax, totalAvatars - maxAvatars, 0); const extraAvatarsElement renderSurplus ? renderSurplus(extraAvatars) : ${extraAvatars};角标本身是一个surplusslot默认elementType: Avatar因此可以通过slots.surplus/slotProps.surplus进一步定制其组件与样式。间距控制spacing prop用spacingprop 改变头像之间的间距可取预设值medium默认或small也可以传自定义数字示例 Spacing.tsxAvatarGroup spacingmedium…/AvatarGroup // 默认 AvatarGroup spacingsmall…/AvatarGroup AvatarGroup spacing{24}…/AvatarGroup源码 AvatarGroup.js 中预设值的实际映射为SPACINGS { small: -16, medium: -8 }即负 margin 实现的叠压效果自定义数字会被取负marginValue -ownerState.spacing传0则完全不叠压。最终值通过 CSS 变量--AvatarGroup-spacing写内联样式每个头像以marginLeft: var(--AvatarGroup-spacing, -8px)消费该变量末位头像marginLeft: 0。与 Badge 组合With badge头像常与Badge组合表达在线状态、未读消息等。官方示例 BadgeAvatars.tsx 展示了三种组合const StyledBadge styled(Badge)(({ theme }) ({ .MuiBadge-badge: { backgroundColor: #44b700, color: #44b700, boxShadow: 0 0 0 2px ${theme.palette.background.paper}, ::after: { position: absolute, top: 0, left: 0, width: 100%, height: 100%, borderRadius: 50%, animation: ripple 1.2s infinite ease-in-out, border: 1px solid currentColor, content: , }, }, keyframes ripple: { 0%: { transform: scale(.8), opacity: 1 }, 100%: { transform: scale(2.4), opacity: 0 }, }, })); const SmallAvatar styled(Avatar)(({ theme }) ({ width: 22, height: 22, border: 2px solid ${theme.palette.background.paper}, })); Stack directionrow spacing{2} {/* 在线状态绿色呼吸点 */} StyledBadge overlapcircular anchorOrigin{{ vertical: bottom, horizontal: right }} variantdot Avatar altRemy Sharp, online src/static/images/avatar/1.jpg / /StyledBadge {/* 未读数量角标 */} Badge overlapcircular anchorOrigin{{ vertical: bottom, horizontal: right }} badgeContent{2} colorprimary Avatar altTravis Howard, 2 unread messages src/static/images/avatar/2.jpg / /Badge {/* 用小型头像作为 badgeContent表示文件的最后编辑者 */} Badge anchorOrigin{{ vertical: bottom, horizontal: right }} badgeContent{SmallAvatar alt src/static/images/avatar/1.jpg /} InsertDriveFileIcon coloraction fontSizelarge titleAccessQ4 budget spreadsheet, last edited by Remy Sharp / /Badge /Stack要点overlapcircular让 Badge 按圆形边界定位角标anchorOrigin控制角标位于右下角第三个示例展示了Badge的badgeContent可以接受任意元素这里是 22px 的SmallAvatar实现文件图标 编辑者小头像的常见模式。头像上传Avatar upload官方示例 UploadAvatars.tsx 展示了完整的头像上传交互用ButtonBase包裹label与隐藏的input typefile选择图片后用FileReader读为 data URL 更新Avatar的srcconst [avatarSrc, setAvatarSrc] React.useStatestring | undefined(undefined); const handleAvatarChange (event: React.ChangeEventHTMLInputElement) { const file event.target.files?.[0]; if (file) { // Read the file as a data URL const reader new FileReader(); reader.onload () setAvatarSrc(reader.result as string); reader.readAsDataURL(file); } }; ButtonBase componentlabel role{undefined} tabIndex{-1} // prevent label from tab focus aria-labelAvatar image sx{{ borderRadius: 40px, :has(:focus-visible): { outline: 2px solid, outlineOffset: 2px }, }} Avatar altUpload new avatar src{avatarSrc} / input typefile acceptimage/* style{{ border: 0, clipPath: inset(50%), height: 1px, margin: -1px, overflow: hidden, padding: 0, position: absolute, whiteSpace: nowrap, width: 1px, }} onChange{handleAvatarChange} / /ButtonBase实现要点ButtonBase渲染为label使点击头像即可触发文件选择tabIndex{-1}防止 label 干扰键盘焦点并通过:has(:focus-visible)在内嵌的input聚焦时显示焦点轮廓保证可访问性input使用经典的隐藏式样式1px clipPath保持不可见但可访问。源码结构小结与相关测试Avatar实现packages/mui-material/src/Avatar/Avatar.js采用 slot 架构root/img/fallback三个 slot 均可通过slots/slotPropsprop 替换组件或注入属性默认根节点为div可通过componentprop 改为其他 HTML 元素或组件工具类packages/mui-material/src/Avatar/avatarClasses.ts 导出MuiAvatar-root、-colorDefault、-circular、-rounded、-square、-img、-fallback七个 class keyAvatarGroup实现packages/mui-material/src/AvatarGroup/AvatarGroup.jsprops 为children、component默认div、max默认 5、renderSurplus、spacing默认medium、total默认children.length、variant默认circular及slots.surplus/slotProps.surplus测试覆盖Avatar.test.js 与 AvatarGroup.test.js 分别验证了回退逻辑与max/total/renderSurplus/spacing的行为可作为各 prop 边界行为的参考依据。综上Avatar通过src/children/variant/sx四个维度覆盖图片、文字、图标头像与尺寸形状定制并以useLoadedhook 提供 SSR 安全的三级回退AvatarGroup则在此之上以负 margin 叠压实现组合头像用max、total、renderSurplus、spacing四个 prop 完整控制显示数量、角标数字、角标内容与间距。以上全部行为均可在 packages/mui-material/src/Avatar 与 packages/mui-material/src/AvatarGroup 目录下直接查证。【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Googles Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考