React Spectrum 进度指示三件套:ProgressBar、ProgressCircle 与 Meter 的 v3 API 设计及 v2 迁移指南 📅 发布时间:2026/9/14 11:36:26 👁 浏览次数: React Spectrum 进度指示三件套ProgressBar、ProgressCircle 与 Meter 的 v3 API 设计及 v2 迁移指南【免费下载链接】react-spectrumA collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.项目地址: https://gitcode.com/GitHub_Trending/re/react-spectrum本文围绕仓库中 specs/api/Progress.md 这份 API 设计规格展开系统讲解 React Spectrum v3 中三个进度指示组件——ProgressBar进度条、ProgressCircle环形进度圈与Meter计量表的完整 Props 契约、默认值行为与无障碍实现并完整保留原文档的 v2 → v3 迁移对照表结合 useProgressBar Hook、ProgressBarBase 基础组件 等源码逐条印证每个 API 决策背后的命名规范与实现原理。读完本文你将能够直接按 v3 API 编写这三个组件、把 v2 代码平滑迁移到 v3并理解 Spectrum 为何如此拆分和命名这些 Props。一、为什么是三件套组件拆分与 API 总览规格文档开篇以 TypeScript 接口形式给出了三个组件的 v3 API 契约这是整份文档的核心骨架interface ProgressBar { value?: number, minValue?: number, maxValue?: number, size?: S | L, label?: ReactNode, aria-label?: string, labelPosition?: top | side, showValueLabel?: boolean, // true by default if label, false by default if not formatOptions?: Intl.NumberFormatOptions, // defaults to formatting as a percentage. valueLabel?: ReactNode, // custom value label (e.g. 1 of 4) variant?: overBackground, isIndeterminate?: boolean } interface ProgressCircle { value?: number, minValue?: number, maxValue?: number, size?: S | M | L, variant?: overBackground, isCentered?: boolean, isIndeterminate?: boolean } interface Meter extends ProgressBar { variant: positive | warning | critical }这段契约本身就体现了 Spectrum 的 API 设计准则见 specs/api/Guidelines.md组件拆分Guidelines 中 Splitting Components 一节规定当组件的选项不再适合放在一起时应拆分为独立组件。v2 中variantpositive | warning | critical本是ProgressBar的变体但这些颜色变体表达的是用户行为造成的量如存储占用告警而非系统操作进度语义完全不同因此 v3 将其拆分为独立的Meter组件——Meter extends ProgressBar正体现了复用进度条的取值 API叠加语义化变体的拆分思路。方向无关命名labelPosition只接受top | side而不接受left/right因为side在 RTL 布局下可自动翻转方向这是 Guidelines 中 Direction Agnostic Naming 规则的直接落地。布尔状态前缀isisIndeterminate表示组件状态按 Boolean Props 规则以is开头。约束型 Props 命名minValue/maxValue在名字尾部带上被约束的属性value而非含糊的min/max符合 Prop Restrictions 规则。各组件的定位差异组件语义底层实现ProgressBar系统操作进度下载、上传、处理有/无确定进度均可ProgressBar.tsxProgressCircle同上但用环形展示常用于悬浮加载态ProgressCircle.tsxMeter已知范围内的量如磁盘容量由用户行为决定而非系统操作Meter.tsxMeter源码中的注释也明确了这一区分Meters are visual representations of a quantity or an achievement. Their progress is determined by user actions, rather than system actions.Meter.tsx L29-L32二、ProgressBar契约、默认值与底层 Hook2.1 默认值与值域钳制对照规格契约ProgressBarBase 中定义了实际的默认值读者可以据此确认各字段的取值行为let { value 0, minValue 0, maxValue 100, size L, label, showValueLabel !!label, // 关键有 label 默认显示数值无 label 默认不显示 labelPosition top, isIndeterminate false, ... } props; value clamp(value, minValue, maxValue); // 越界值会被钳制几个要点值得注意showValueLabel的条件默认值契约中注释 true by default if label, false by default if not 在源码中正是showValueLabel !!label一行实现——只有给了可见标签时才默认展示百分比数值避免无标签场景下孤悬的数字。value会被钳制clamp传入value: -1会得到0传入value: 1000会得到maxValue。测试用例 ProgressCircle.test.js 中的 clamps values to 0 / clamps values to 100 两个it用例专门验证了这一点。进度填充宽度的计算let range maxValue - minValue; let percentage range 0 ? 0 : (value - minValue) / range; barStyle.width ${Math.round(percentage * 100)}%;即填充宽度是(value - minValue) / (maxValue - minValue)四舍五入到整数百分比并特判了range 0minValue maxValue时宽度为 0 的除零边界。2.2 无障碍 HookuseProgressBarProgressBar组件本体很薄ProgressBar.tsx L24-L44真正计算 ARIA 属性的逻辑全部在 useProgressBarvalue clamp(value, minValue, maxValue); let range maxValue - minValue; let percentage range 0 ? 0 : (value - minValue) / range; let formatter useNumberFormatter(formatOptions); if (!isIndeterminate !valueLabel) { let valueToFormat formatOptions.style percent ? percentage : value; valueLabel formatter.format(valueToFormat); } return { progressBarProps: mergeProps(domProps, { ...fieldProps, aria-valuenow: isIndeterminate ? undefined : value, aria-valuemin: minValue, aria-valuemax: maxValue, aria-valuetext: isIndeterminate ? undefined : (valueLabel as string), role: progressbar }), labelProps };这里印证了规格契约中formatOptions与valueLabel两个 v3 新增字段的完整语义formatOptions默认是{ style: percent }契约注释 defaults to formatting as a percentage此时数值标签对percentage0~1 的比例做格式化若用户传入了其他Intl.NumberFormatOptions如{ style: decimal, maximumFractionDigits: 2 }则对原始value做格式化——这就是注释里 others can also be supported 的实现路径。valueLabel用于 1 of 4 这类自定义数值文案当提供了valueLabel时不再走 formatter直接把该 ReactNode 作为aria-valuetext输出。isIndeterminate时不输出aria-valuenow与aria-valuetext符合 WAI-ARIA 对不确定进度条的要求测试 ProgressCircle.test.js 中 handles indeterminate 用例正是断言aria-valuenow属性不存在。标签元素使用labelElementType: span而非label因为进度条不是可聚焦的 HTML 表单控件见 useProgressBar.ts L89-L94。2.3 渲染结构与数值标签的联动ProgressBarBase.tsx L102-L132 渲染出label → valueLabel → track → fill的结构其中数值标签直接复用 ARIA 侧算好的结果{showValueLabel barProps ( div className{classNames(styles, spectrum-BarLoader-percentage)} {barProps[aria-valuetext]} /div )}即视觉上显示的百分比文本与aria-valuetext保证一致。labelPositionside会切换到spectrum-BarLoader--sideLabel类sizeS/L分别映射--small/--large修饰类。另外注意variantoverBackground在 v3 源码中已标注deprecated推荐使用staticColorwhite | black替代ProgressBarBase.tsx L46-L52——这是规格契约中variant字段的一个演进细节。三、ProgressCircle双掩膜旋转实现环形填充ProgressCircle在取值契约上与ProgressBar相同value/minValue/maxValue默认 0/0/100见 ProgressCircle.tsx L22-L45差异在规格表中列出的两处size支持S | M | L三档默认M进度条只有两档默认L新增isCentered与minValue/maxValue迁移表见第五节。源码上最有趣的部分是它没有使用 SVG 弧线而是用两块 CSS 掩膜旋转来画出任意角度的环形填充ProgressCircle.tsx L94-L107let range maxValue - minValue; let percentage range 0 ? 0 : ((value - minValue) / range) * 100; let angle; if (percentage 0 percentage 50) { angle -180 (percentage / 50) * 180; subMask1Style.transform rotate(${angle}deg); subMask2Style.transform rotate(-180deg); } else if (percentage 50) { angle -180 ((percentage - 50) / 50) * 180; subMask1Style.transform rotate(0deg); subMask2Style.transform rotate(${angle}deg); }原理可以推断为一个 360° 圆环拆成上下两个半圆掩膜DOM 中对应fillSubMask1/fillSubMask2两个子元素见 ProgressCircle.tsx L134-L151。进度在前半圈0~50%时只旋转掩膜 1超过 50% 时掩膜 1 定格在 0°盖满上半圈掩膜 2 继续旋转覆盖下半圈。每块掩膜一次最多转 180°正好对应各自半圆的范围。isIndeterminate为true时不设置任何旋转交给 Spectrum CSS 的spectrum-CircleLoader--indeterminate类播放旋转动画。可访问性方面ProgressCircle复用同一个useProgressBarHook第 90 行传入{...props, value}因此 ARIA 输出与进度条完全一致并且在非生产环境下若既无aria-label也无aria-labelledby会主动告警ProgressCircle.tsx L109-L113进度条侧的等价告警在 ProgressBarBase.tsx L96-L100。四、Meter继承 ProgressBar 契约并叠加语义变体规格定义interface Meter extends ProgressBar { variant: positive | warning | critical }。实际实现上Meter.tsx 复用了ProgressBarBase作为渲染骨架只替换了 ARIA Hook 与修饰类export interface SpectrumMeterProps extends SpectrumProgressBarBaseProps { variant?: informative | positive | warning | critical; // default informative } export const Meter React.forwardRef(function Meter(props, ref) { let {variant informative, ...otherProps} props; const {meterProps, labelProps} useMeter(props); return ( ProgressBarBase {...otherProps} barProps{meterProps} barClassName{classNames(styles, { is-positive: variant positive, is-warning: variant warning, is-critical: variant critical })} / ); });两个源码层面的补充说明默认变体是informative中性蓝规格表列出的positive/warning/critical是三个强调状态informative时不加任何修饰类走 Spectrum CSS 默认样式。ARIA role 使用双值meter progressbaruseMeter 内部先调用useProgressBar再把 role 改写为meter progressbar源码注释解释了原因——部分浏览器注释中提到 Chrome 会自动回退、Firefox 当时不支持 meter、Safari 13 支持对meterrole 支持不一写两个 role 值可以让屏幕阅读器在新旧环境下都能得到正确语义。Meter 的测试见 Meter.test.js 与 SSR 场景下的 Meter.ssr.test.js。五、v2 → v3 迁移对照表完整继承自规格文档5.1 ProgressBar Changesv2v3NotesProgressProgressBarsizeMsizeLspectrum calls it large, not mediumlabelPositionleftlabelPositionsidertl supportlabelPositionbottom-not supported.showPercentshowValueLabeldefault changed to true if label is specified, false if not.-numberFormatteradded. default is percentage, but others can also be supported.-valueLabelcustom value label, e.g. 1 of 4-isIndeterminateaddedminminValuemaxmaxValuevariantpositiveMeter variantpositivevariantwarningMeter variantwarningvariantcriticalMeter variantcritical逐条解读这些改动的动机sizeM→sizeLSpectrum 设计系统把进度条的两档尺寸命名为 small/largev2 的 M 在 v3 词汇表中对应 L直接改名避免语义错位。labelPositionleft→sideside是方向无关术语RTL 下自动指右侧这正是第五节开头提到的 Guidelines 规则bottom则被整体取消支持。showPercent→showValueLabel改名后语义更泛——显示的不只是百分比受formatOptions影响默认值逻辑也变为有 label 默认开、无 label 默认关与 ProgressBarBase 中showValueLabel !!label完全对应。numberFormatter→ 源码中的formatOptions规格表中 v3 新增项写作numberFormatteradded. default is percentage, but others can also be supported当前源码将其演进为formatOptions?: Intl.NumberFormatOptions直接透传给Intl.NumberFormat默认{ style: percent }与规格意图一致且接口更标准。min/max→minValue/maxValue约束型 Props 带上被约束属性名消除歧义。variant三兄弟整体迁往Meter语义拆分ProgressBar保留variant: overBackground叠加在彩色背景上的变体现已标记 deprecated推荐staticColor。5.2 ProgressCircle Changesv2v3NotesWaitProgressCirclevariantindeterminateisIndeterminatecenteredisCentered-minValueadded-maxValueaddedWait→ProgressCircle组件从等待指示器升级为通用进度指示器因此可以像ProgressBar一样表达确定进度value/minValue/maxValue。variantindeterminate→isIndeterminate不确定态是组件状态而非视觉风格按 Guidelines 以is前缀的布尔属性表达variant一词留给真正的视觉变体overBackground。centered→isCentered同类改名布尔状态统一is前缀。新增minValue/maxValue与ProgressBar的取值契约对齐源码中 ProgressCircle 的取值段 与进度条完全同构。六、安装与使用入口、导出与最小示例三个组件通过adobe/react-spectrum主包导出导出映射见 exports/ProgressBar.ts 与 exports/Meter.ts独立子包入口 packages/react-spectrum/progress/src/index.ts 则额外导出了可复用的ProgressBarBase与类型供二次封装时复用同一渲染骨架。import {Provider, ProgressBar, ProgressCircle, Meter} from adobe/react-spectrum; export function Example() { return ( Provider {/* 确定进度0-100默认按百分比显示数值标签 */} ProgressBar label正在上传照片… value{72} / {/* 自定义范围与数值文案1 of 4 风格 */} ProgressBar minValue{1} maxValue{4} value{2} valueLabel第 2 步共 4 步 label安装进度 / {/* 不确定进度不渲染 aria-valuenow播放滑动动画 */} ProgressBar aria-label正在加载 isIndeterminate / {/* 环形加载器需自行提供可达性标签 */} ProgressCircle aria-label处理中 value{30} sizeL / {/* 计量表磁盘空间告警 */} Meter label存储空间 value{82} variantcritical / /Provider ); }上述示例中的行为均可在源码中找到依据value{72}会被钳制后映射为 72% 填充宽度valueLabel同时驱动视觉文本与aria-valuetextisIndeterminate时aria-valuenow为undefinedMeter的variantcritical落到is-critical修饰类。配套资源官方文档页ProgressBar.mdx、ProgressCircle.mdx、Meter.mdx交互示例Storybookstories/progress、stories/meter视觉回归用例chromatic/progress、chromatic/meter测试ProgressBar.test.js含负值钳制、缺失 aria-label 告警、自定义 DOM props 透传等用例、ProgressCircle.test.js、Meter.test.js七、小结specs/api/Progress.md 用不到 70 行定义清晰划定了 v3 进度指示组件族的 API 边界ProgressBar承载系统操作进度含不确定态与可定制数值格式化、ProgressCircle提供环形形态与三档尺寸、Meter从进度条契约中派生出面向量的语义变体。对照源码可以确认这份契约并非纸面设计——每个字段从showValueLabel的条件默认值、clamp钳制到rolemeter progressbar的浏览器兼容策略都能在 ProgressBarBase、useProgressBar、useMeter 中找到逐行对应的实现且被 test/progress 与 test/meter 下的测试用例持续验证。对于仍在 v2 上的项目按第五节两张迁移表逐项替换组件改名、min/max补全为minValue/maxValue、variant强调态迁往Meter、centered/indeterminate补is前缀即可完成平滑升级。【免费下载链接】react-spectrumA collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.项目地址: https://gitcode.com/GitHub_Trending/re/react-spectrum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考