跳到主要内容

Loading 加载

Spinner 表示无法量化的等待;CircularProgress 表示已知的 0..1 确定进度。圆形进度默认只显示圆环,通过 showLabel 才在中央显示取整百分比。

代码演示

下方使用公共 <Spinner> API;本页由 Spinner.web.tsx 的静态 CSS keyframes 驱动,native 则由 Spinner.tsx 的 Reanimated worklet 驱动。

确定进度
尺寸
颜色
thickness · 描边粗细

用法

import { CircularProgress, Spinner } from '@unif/react-native-design';
import { useColors } from '@unif/react-native-design';

function Demo() {
const c = useColors();
return (
<>
<CircularProgress value={0.42} />
<CircularProgress
value={0.68}
showLabel
accessibilityLabel="图片上传进度"
/>
<Spinner /> {/* 默认 18px 主橙 */}
<Spinner size={24} /> {/* 24px */}
<Spinner color={c.success} /> {/* 绿色 */}
<Spinner color={c.foregroundSubtle} thickness={1.5} /> {/* 细线灰 */}
<Spinner
size={24}
style={{ width: 72, height: 48, transform: [{ scale: 1.2 }] }}
/>{' '}
{/* outer 可扩容/变换,24pt ring 仍居中旋转 */}
</>
);
}

API

CircularProgress

中央百分比按 ThemeProvider.fontScale 缩放一次,系统字号沿 RN 原生行为处理。文字自然布局,圆环与描边保持 size / thickness 指定的实际尺寸。小圆环配大字号时外层占用可以增大;不会缩小字号、截掉百分号或自动关闭标签。showLabel=false 时保持 size × size 的固定占用;显示标签时采用平台固有尺寸,纵向容器的 stretch 不会把圆环拉成整行宽。外部显式设置裁切的容器仍由调用方负责。

未知上传比例应使用 Spinner 或 BorderBeam 配合实际状态说明,不传 NaN 或伪造 0%;100% 也不代表业务成功。

参数类型默认值说明
valuenumber必填0..1 的确定进度;越界值收敛到边界,非有限值按 0
sizenumber?32圆环直径;非有限或 < 16 钳到 16
thicknessnumber?2描边宽度;无效值 fallback 到 2,最大不超过直径一半
colorstring?c.primary已完成圆弧颜色
trackColorstring?c.outline未完成轨道颜色
showLabelboolean?false是否在中央显示取整后的百分比
labelColorstring?c.foreground中央百分比文字颜色
accessibilityLabelstring?进度progressbar 的可访问性名称
styleStyleProp<ViewStyle>?外层布局样式
testIDstring?E2E / 测试定位

Spinner

参数类型默认值说明
sizenumber?18直径(含 stroke);非有限或 < 8 钳到 8(打 warn)
colorstring?c.primary(运行期 hook 取)旋转弧颜色(轨道色固定 c.outline
thicknessnumber?2描边粗细;≤ 0 fallback 到 2
styleStyleProp<ViewStyle>?outer layout 样式;可扩容并使用 margin/flex/position/transform,不能改变 inner ring 居中
testIDstring?E2E / 测试定位

无障碍(a11y)

CircularProgress 自身暴露 progressbarmin=0max=100、当前整数百分比与文字值;内部 SVG 和可选中央文字对辅助技术隐藏,避免重复朗读。业务侧应传入能描述对象的 accessibilityLabel,例如“图片上传进度”。

来源:src/components/ui/CircularProgress/src/components/ui/Spinner/

Spinner 是纯视觉旋转指示器,源码刻意把自己对 SR 隐藏:两端 outer View 统一展开完整隐藏属性(accessible={false}accessibilityElementsHiddenimportantForAccessibility="no-hide-descendants"aria-hidden)。它accessibilityRole='progressbar'、也不设 accessibilityState={{ busy }}accessibilityLabel

因此「正在加载」的语义必须由外部上下文声明 —— 例如在包裹容器上设 accessibilityState={{ busy: true }}、或用一段状态文案(如 <Text>加载中…</Text> / live region)告知 SR;不要指望 Spinner 自身朗读。组件本身无可配置的 a11y prop(仅 size / color / thickness / style / testID)。

节奏

900ms 一圈,线性 easing,不要回弹。这是设计令牌 motion 之外的特例——加载体感需要稳定均匀。

两层容器语义

native 与 Web 都固定为 outer layout View + inner visual ring:

  • outer 先提供 safeSize × safeSize 默认尺寸,再应用 caller style,最后强制 alignItems / justifyContent: center;它独占 testID、完整 a11y 隐藏和 caller transform。
  • inner 始终保持 safeSize × safeSize,只承载 ring 与 rotate。native 的 Reanimated transform、Web 的 CSS animation ref 都只落到 inner。
  • caller 可把 outer 扩到更大,也可添加 scale / translate;这些 transform 不会被旋转覆盖。caller 的 alignItems / justifyContent 不能把 ring 推离中心。
  • Web @keyframes 内容是静态常量,不拼接 size、color、thickness 或其他 props。

Spinner 属于 essential motion;系统开启 reduced motion 时仍保持匀速旋转,加载状态的非动画语义仍应由外部状态文案或 busy 容器提供。

人工验收状态

runtime harness 已提供扩大 outer、caller scale/translate、恶意 align/justify 与 inner rotate 的组合检查。真实 native / Web Inspector 尚未执行,因此当前仍为 BLOCKED