跳到主要内容
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
开始使用WebReactSwiftUIJetpack ComposeFlutterReact Native预设你自己的设计系统与 AI 代理一起使用 Tanrei品牌设计令牌设计系统与定制Theme Studio颜色字体排印图标形状层级动效交互状态行为布局无障碍文字颜色令牌字阶令牌形状令牌层级令牌动效令牌状态令牌间距与度量令牌组件按钮主操作按钮分段按钮按钮组带菜单的按钮工具栏复制按钮复选框单选按钮开关纸片(Chip)菜单滑块滑块字段日历DateTimePicker字段组单选组复选框组下拉选择框下拉选择字段菜单栏文件上传文件选择器 进阶放置区文本输入框 进阶文本字段纸片字段字段组合框 进阶组合框字段数字输入框 进阶数字字段颜色输入框 进阶颜色字段一次性验证码输入框 进阶一次性验证码字段嵌入式文本输入框 进阶聊天输入栏卡片分隔线模态层 进阶对话框列表列表行底部面板侧边面板轮播代码块表格弹出框步骤折叠面板描述列表图示应用外壳页头树视图时间线图片预览调整手柄可调整大小的框滚动区域徽标进度指示器计量条数字滚动器加载指示器消息条(Snackbar)工具提示键盘快捷键标签(Tag)行内代码提示块(Callout)头像空状态标签组(Tag group)骨架屏文件项色块跑马灯文本标签页标签页视图应用栏导航栏导航抽屉搜索链接分页上一页/下一页链接步骤条面包屑目录链接卡片FAB 菜单状态Tanrei 是如何构建的参与贡献许可与致谢
首页组件进度指示器

进度指示器

以条形或圆环显示任务已经进行到哪里;进度未知时,则表示任务正在进行。

content_copy以 Markdown 格式查看
类别
信息传达
令牌
comp.progress-indicator
层级
基础

概览

Web
tanrei-progress-indicator
schedule
SwiftUI
TanreiProgressIndicator
schedule
Jetpack Compose
TanreiProgressIndicator
schedule
Flutter
TanreiProgressIndicator
schedule
React Native
ProgressIndicator
schedule

生成自 docs/status/status.json, specs/progress-indicator.spec.json

线性和环形

用法

Web 和 React 的示例展示的是当前的规格。SwiftUI、Compose、Flutter 和 React Native 待跟进:它们还没有 separation,示例展示的是它们实际提供的内容。

<tanrei-progress-indicator value="0.4" label="正在上传 report.pdf"></tanrei-progress-indicator>
<tanrei-progress-indicator value="3" max="10" value-text="第 3 个文件,共 10 个" label="正在上传"></tanrei-progress-indicator>

<!-- 没有 value:不确定,与没有 value 的 <progress> 一样。 -->
<tanrei-progress-indicator variant="circular" size="small" label="正在同步"></tanrei-progress-indicator>

<!-- 指示器与轨道其余部分之间留一段间隙,不管设计系统选择的是什么。 -->
<tanrei-progress-indicator separation="gap" value="0.4" label="正在上传 report.pdf"></tanrei-progress-indicator>

<script type="module">
  import '@tanrei/web/components/progress-indicator';
  const bar = document.querySelector('tanrei-progress-indicator');
  bar.value = sent / total;
</script>
import { ProgressIndicator } from '@tanrei/react';

<ProgressIndicator value={sent / total} label="正在上传 report.pdf" />
<ProgressIndicator value={files} max={10} valueText={`第 ${files} 个文件,共 10 个`} label="正在上传" />
<ProgressIndicator variant="circular" size="small" value={null} label="正在同步" />
<ProgressIndicator separation="gap" value={sent / total} label="正在上传 report.pdf" />
import Tanrei

TanreiProgressIndicator("正在上传 report.pdf", value: sent / total)
TanreiProgressIndicator("正在上传", value: Double(files), in: 0...10, currentValueLabel: "第 \(files) 个文件,共 10 个")

// 没有 value(默认)就是不确定。
TanreiProgressIndicator("正在同步", variant: .circular, size: .small)
import io.github.chnotchy.tanrei.components.TanreiProgressIndicator
import io.github.chnotchy.tanrei.components.TanreiProgressIndicatorVariant
import io.github.chnotchy.tanrei.tokens.TanreiProgressIndicatorSize

// progress 是一个 lambda:新的值只触发重绘,不触发重组。
TanreiProgressIndicator(progress = { sent / total }, contentDescription = "正在上传 report.pdf")
TanreiProgressIndicator(
    progress = { files.toFloat() },
    contentDescription = "正在上传",
    valueRange = 0f..10f,
    stateDescription = "第 $files 个文件,共 10 个",
)

// 不带 progress 的重载是不确定的。
TanreiProgressIndicator(
    contentDescription = "正在同步",
    variant = TanreiProgressIndicatorVariant.Circular,
    size = TanreiProgressIndicatorSize.Small,
)
import 'package:tanrei/tanrei.dart';

TanreiProgressIndicator(value: sent / total, semanticsLabel: '正在上传 report.pdf');
TanreiProgressIndicator(
  value: files.toDouble(),
  max: 10,
  semanticsValue: '第 $files 个文件,共 10 个',
  semanticsLabel: '正在上传',
);

// value: null(默认)就是不确定。
const TanreiProgressIndicator(
  variant: TanreiProgressIndicatorVariant.circular,
  size: TanreiProgressIndicatorSize.small,
  semanticsLabel: '正在同步',
);
import { ProgressIndicator } from '@tanrei/react-native';

<ProgressIndicator accessibilityLabel="正在上传 report.pdf" value={sent / total} />
<ProgressIndicator accessibilityLabel="正在上传" value={files} max={10} valueText={`第 ${files} 个文件,共 10 个`} />
<ProgressIndicator accessibilityLabel="正在同步" variant="circular" size="small" />

规格

各种尺寸、范围的两端,以及自定义范围

分隔方式:divider(默认系统的选择)和 gap

构成

构成

绘制在 Web 包所绘的测试样例 linear-determinate 之上。
部件 说明
1 track The full length of the bar, or the full circle of the ring: the whole of the task.
2 indicator The part of the track drawn in the indicator color: the fraction done (determinate), or a moving segment or arc (indeterminate). It meets the rest of the track, or stands a gap from it (separation).
3 stop-indicator · 可选 A dot at the end of a determinate bar's track that marks where the task ends. Drawn under either separation whenever its size is above 0; 0 draws none.

属性

属性 类型 默认值 说明
variant linear | circular "linear" Linear is a bar that fills the width its layout gives it, for content and pages; circular is a ring of a fixed size, for tight places and next to text. A structural choice drawn by code, not a token axis: a design system cannot add a form by values.
size small | medium | large "medium" The bar's thickness (2 / 4 / 8) or the ring's box (16 / 24 / 40). A design system may add values.
separation auto | divider | gap "auto" How a determinate indicator is told from the rest of its track. divider: it is drawn on the track with no space between them. gap: empty space is left between them. Whether a bar marks the track's end with a dot is comp.progress-indicator.stop-indicator.size, not the separation. Auto takes comp.progress-indicator.track.separation, the design system's answer (divider in the default system).
value number null How far the task has come, between min and max. Null makes the indicator indeterminate.
min number 0 The value at which nothing is done.
max number 1 The value at which the task is done.
label string "" The accessible name: what is in progress ("Uploading report.pdf"). Required: a bar alone is not a name.
valueText string "" How assistive technology reads the value when a percentage is not the natural reading ("3 of 10 files"). Empty reads the platform's percentage.

状态

行为

以 EARS 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。

ID 需求
PRG-1 WHILE value is a number THE progress indicator SHALL be determinate and draw the indicator over the fraction (value − min) / (max − min) of the track, from the track's start: the inline-start of a bar, the top of a ring, turning clockwise.
PRG-2 WHILE value is null THE progress indicator SHALL be indeterminate: a bar SHALL move a segment of the indicator, two fifths of the track long, from beyond the track's start to beyond its end once per comp.progress-indicator.motion.cycle-duration with comp.progress-indicator.motion.cycle-easing; a ring SHALL turn an arc of the indicator a quarter of the circle long clockwise once per comp.progress-indicator.motion.cycle-duration at constant speed (sys.motion.easing.linear).
PRG-3 THE progress indicator SHALL draw a bar comp.progress-indicator.linear.thickness.medium high for medium (and so on for each size) across the full inline size its layout gives it, and a ring whose outer edge fills a comp.progress-indicator.circular.size.medium square with a stroke of comp.progress-indicator.circular.thickness.medium for medium (and so on for each size).
PRG-4 THE progress indicator SHALL draw the track in comp.progress-indicator.track.color and the indicator over it in comp.progress-indicator.indicator.color; the ends of a bar's track and indicator SHALL be rounded by comp.progress-indicator.track.shape, clamped to half the thickness, and a ring's arcs SHALL have round caps unless comp.progress-indicator.track.shape is sys.shape.corner.none.
PRG-5 WHILE determinate, its separation is gap (PRG-17) and value lies strictly between min and max THE progress indicator SHALL leave comp.progress-indicator.track.gap of empty space, measured along the track, between the indicator and the rest of the track — on a bar between the indicator's end and the track, on a ring at both ends of the indicator's arc. WHILE its separation is divider THE progress indicator SHALL draw the indicator on the track with no empty space between them: the edge where the indicator's color ends is what divides them. WHILE a bar is determinate THE progress indicator SHALL draw, under either separation, a dot of comp.progress-indicator.stop-indicator.size, and no larger than the bar is thick, in comp.progress-indicator.indicator.color centered in the thickness with its center half the thickness before the track's end (concentric with the track's rounded end); it SHALL NOT be drawn while value is at max, where the indicator covers it, and a size of 0 SHALL draw none. Whether the dot is drawn is independent of the separation.
PRG-6 WHEN value changes while determinate THE progress indicator SHALL move the indicator's end to the new value over comp.progress-indicator.motion.value-duration with comp.progress-indicator.motion.value-easing; a switch between determinate and indeterminate SHALL take effect at once.
PRG-7 WHERE reduced motion is requested THE progress indicator SHALL show a value change at once, and WHILE indeterminate SHALL NOT move: it SHALL draw the indicator over the whole track (the whole circle of a ring) and ease its opacity from 1 to 0 and back to 1 once per comp.progress-indicator.motion.cycle-duration with comp.progress-indicator.motion.cycle-easing, over a track that stays still.
PRG-8 WHILE value is a number THE progress indicator SHALL expose the progressbar role to assistive technology with label as its name, min and max as its range, the clamped value as its current value, and valueText as the value's text when valueText is not empty.
PRG-9 WHILE value is null THE progress indicator SHALL expose the progressbar role with label as its name, no current value, and the platform's busy or indeterminate state.
PRG-10 THE progress indicator SHALL NOT be focusable and SHALL NOT announce value changes by itself; assistive technology SHALL read the current value when it reaches the indicator, and a platform that marks frequently updating elements SHALL mark it so.
PRG-11 IF value is below min or above max THE progress indicator SHALL draw and expose it clamped to that range; IF max is not greater than min THE progress indicator SHALL draw the track with no indicator and expose a current value of 0 in a range of 0 to 1; in each case debug builds SHALL report the values.
PRG-12 WHEN size names a value that the active design system does not have, or variant or separation a value the component does not define, THE progress indicator SHALL render with the default value's tokens (medium, linear, auto), and in debug builds report the unknown value.
PRG-13 WHERE the layout direction is right-to-left THE progress indicator SHALL fill a bar and move its indeterminate segment from right to left; a ring SHALL NOT be mirrored: it starts at the top and turns clockwise in every direction, as a clock does.
PRG-14 WHILE the progress indicator is not visible (scrolled out of view, in a hidden view, or in an app in the background) THE progress indicator SHALL NOT run its indeterminate animation, and SHALL resume it when it becomes visible.
PRG-15 WHILE label is empty THE progress indicator SHALL still render, and in debug builds report that it has no accessible name.
PRG-16 THE progress indicator SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the progress indicator is shown THE progress indicator SHALL draw itself for the new direction at once, without a motion of its own, keeping its value, and the place of an indeterminate segment in its cycle.
PRG-17 THE progress indicator SHALL tell a determinate indicator from the rest of its track as its separation names: WHILE separation is auto, as comp.progress-indicator.track.separation names, which follows sys.behavior.separation unless a design system sets it apart; WHILE separation is divider or gap, as that value names, whatever the tokens say; each as PRG-5 draws it. WHEN separation, or the token it follows, changes THE progress indicator SHALL draw the other form at once, without animation, keeping its value. WHILE indeterminate THE progress indicator SHALL be drawn the same under either (PRG-2, PRG-7). A platform that cannot draw a choice SHALL fall back to the first value, divider, and state that in its README.
PRG-18 WHERE the platform has ARIA attributes (the web), the host carries its progressbar role (PRG-8) itself, so what the product writes on it is not moved, but for a name: WHILE label is not empty THE progress indicator SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that label is its whole name although the platform gives aria-labelledby the precedence, and WHEN label is emptied SHALL give them back as they were written; WHILE label is empty a name the product gives so — aria-label, or aria-labelledby naming an element — SHALL be its name and SHALL NOT be reported as having none in debug builds (PRG-15). WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaLabelledByElements) to null, THE progress indicator SHALL take it away.

各平台名称对照

对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。

规格 Web SwiftUI Jetpack Compose Flutter React Native
组件 tanrei-progress-indicator TanreiProgressIndicator TanreiProgressIndicator TanreiProgressIndicator ProgressIndicator
variant · 属性 variant variant variant variant variant
size · 属性 size size size size size
separation · 属性 separation separation separation separation separation
value · 属性 value value progress value value
min · 属性 min bounds valueRange min min
max · 属性 max bounds valueRange max max
label · 属性 label label contentDescription semanticsLabel accessibilityLabel
valueText · 属性 valueText currentValueLabel stateDescription semanticsValue valueText
备注 As a native <progress>: no value attribute (or removing it) is indeterminate. valueText is the value-text attribute. The host carries role="progressbar" and the aria-value attributes. separation is an attribute (auto, divider, gap); auto reads the name in --tanrei-comp-progress-indicator-track-separation. Initialisers follow ProgressView: TanreiProgressIndicator(_ label:, value: Double?, in bounds: ClosedRange<Double> = 0...1, currentValueLabel:). value: nil is indeterminate; bounds carries min and max as Slider's in: does. Not built on this platform yet: separation (auto, divider, gap), PRG-17 and the seven gap and divider fixtures; reading comp.progress-indicator.track.separation as a typed choice needs the token emitters to write a choice of kind as a typed enumeration, which they do not yet. Not built on this platform yet: the stage color a fixture's surface names, which the linear-on-container-highest fixture draws on. Two overloads, as Material's indicators have: with progress: () -> Float it is determinate (a lambda, so a changing value redraws without recomposing), read within valueRange: ClosedFloatingPointRange<Float> = 0f..1f (as Slider names it); without progress it is indeterminate. stateDescription is the semantics property of the same name. Not built on this platform yet: separation (auto, divider, gap), PRG-17 and the seven gap and divider fixtures; reading comp.progress-indicator.track.separation as a typed choice needs the token emitters to write a choice of kind as a typed enumeration, which they do not yet. Not built on this platform yet: the stage color a fixture's surface names, which the linear-on-container-highest fixture draws on. As Flutter's ProgressIndicator: value: null is indeterminate, and semanticsLabel / semanticsValue are its names. Unlike Flutter's (a fraction from 0 to 1), value is read between min and max, as Slider does. Not built on this platform yet: separation (auto, divider, gap), PRG-17 and the seven gap and divider fixtures; reading comp.progress-indicator.track.separation as a typed choice needs the token emitters to write a choice of kind as a typed enumeration, which they do not yet. Not built on this platform yet: the stage color a fixture's surface names, which the linear-on-container-highest fixture draws on. value undefined or null is indeterminate. min, max, value and valueText are passed together as accessibilityValue ({ min, max, now, text }); indeterminate sets accessibilityState={{ busy: true }} and no now. Not built on this platform yet: separation (auto, divider, gap), PRG-17 and the seven gap and divider fixtures; reading comp.progress-indicator.track.separation as a typed choice needs the token emitters to write a choice of kind as a typed enumeration, which they do not yet. Not built on this platform yet: the stage color a fixture's surface names, which the linear-on-container-highest fixture draws on.

令牌

令牌 引用 解析值(浅色)
--tanrei-comp-progress-indicator-circular-size-large — 40px
--tanrei-comp-progress-indicator-circular-size-medium — 24px
--tanrei-comp-progress-indicator-circular-size-small — 16px
--tanrei-comp-progress-indicator-circular-thickness-large sys.border.thick 2px
--tanrei-comp-progress-indicator-circular-thickness-medium sys.border.thick 2px
--tanrei-comp-progress-indicator-circular-thickness-small sys.border.thick 2px
--tanrei-comp-progress-indicator-indicator-color sys.color.primary #1b1b1b
--tanrei-comp-progress-indicator-linear-thickness-large — 8px
--tanrei-comp-progress-indicator-linear-thickness-medium — 4px
--tanrei-comp-progress-indicator-linear-thickness-small — 2px
--tanrei-comp-progress-indicator-motion-cycle-duration sys.motion.duration.extra-long4 1000ms
--tanrei-comp-progress-indicator-motion-cycle-easing sys.motion.easing.standard cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-progress-indicator-motion-value-duration sys.motion.duration.medium2 300ms
--tanrei-comp-progress-indicator-motion-value-easing sys.motion.easing.standard cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-progress-indicator-stop-indicator-size sys.space.space0 0px
--tanrei-comp-progress-indicator-track-color sys.color.surface-variant #eeeeee
--tanrei-comp-progress-indicator-track-gap sys.space.space50 4px
--tanrei-comp-progress-indicator-track-separation sys.behavior.separation 0
--tanrei-comp-progress-indicator-track-shape sys.shape.corner.full 9999px

测试样例

作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。

生成自 specs/progress-indicator.spec.json, tokens/comp/progress-indicator.tokens.json, design/tokens/

指南

何时使用

进度指示器用于耗时长到会被注意到的任务:上传、导入、部署、耗时较长的保存。只要你能测量进度,哪怕只是粗略地测量,就显示任务已经进行到哪里;面对一个会前进的进度条,人们等得更有耐心。暂时无法测量时,把它显示为不确定的形式,一旦有了数值就切换为显示数值。

对于时长无法测量的短暂等待,例如列表正在获取下一页,请改用加载指示器。它没有轨道,因为它从不承诺一个数值。

如何选择变体

变体 用途
线性 属于某个页面或某个区域的任务:页面顶部、卡片内部、上传列表中某个文件的下方。进度条会占满布局分配给它的宽度。
环形 空间紧凑的地方,以及文本旁边的进度:按钮里、列表行里、小面板里。圆环的尺寸是固定的。

如何选择尺寸

尺寸 默认系统中的进度条粗细 / 圆环尺寸 用途
Small 2 / 16 紧凑的行、控件内部、小号文本旁边。
Medium 4 / 24 大多数地方。这是默认尺寸。
Large 8 / 40 整个视图所围绕的那一项任务,或者等待内容的空白区域。

分隔线还是间隙

确定形式的指示器与轨道的其余部分有两种区分方式。使用 divider 时,指示器直接画在轨道上,两者之间没有空隙。使用 gap 时,指示器之后会留出一小段空白——圆环上是在圆弧的两端。不确定形式的指示器在两种方式下外观相同。进度条是否用一个圆点标出轨道的终点是另一项选择,由停止指示器的尺寸决定:为它设定了尺寸的设计系统在两种分隔方式下都会画出圆点,默认系统则不画。

设计系统为所有具有这项选择的组件统一选择一次,默认系统选择的是 divider。让 separation 保持 auto 就会遵循这一选择,这样指示器与周围的按钮组和菜单保持一致。只有当某一个指示器有理由与众不同时,才在它上面设置 divider 或 gap。

推荐

  • 给它一个说明正在进行什么的标签:写“正在上传 report.pdf”,不写“进度”。如果它旁边显示了说明文字,请使用同样的措辞。
  • 百分比读起来不自然时,使用数值文本:“第 3 个文件,共 10 个”“第 2 步,共 4 步”。
  • 任务结束时,用文字告诉用户:一条说明任务已完成的消息,或者说明出了什么问题以及接下来该怎么做。

避免

  • 不要用颜色来报告失败。对看不到颜色的人来说,红色的进度条什么也没说;应当停止指示器,并在它旁边显示一条消息。
  • 不要把它当作显示某个水平的量表,例如已用存储空间或密码强度。那是计量条(meter),是另一个组件。
  • 不要让数值倒退,也不要在任务完成之前跳到终点。进度条是对剩余等待时间的承诺。
  • 不要为同一项任务显示多个指示器。为整体显示一个,或者为用户关心的每一项各显示一个。

无障碍

角色
progressbar
标注
label is the accessible name. valueText, when given, replaces the platform's percentage as the spoken value. A visible caption next to the indicator is the product's composition; when it exists, use the same words as label.
最小目标
即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px

键盘操作

按键 动作

备注

  • Not interactive: the indicator takes no input and is never focusable (PRG-10), so the minimum target holds without extending anything.
  • The value is read from the boundary between indicator and track: comp.progress-indicator.indicator.color must stay at least 3:1 against comp.progress-indicator.track.color and against the surface behind it, under divider and under gap alike. A faint track may leave the track's end hard to see; the stop indicator, in a system that gives it a size, marks it.
  • Indeterminate is announced as busy with no value: on the web a progressbar without aria-valuenow (and aria-busy), on Apple platforms a progress element with no value, on Android the indeterminate range info, in Flutter a progress indicator with a null value.
  • Reduced motion replaces movement with an opacity pulse of at most one per second (PRG-7), below the three-per-second flash threshold.

生成自 specs/progress-indicator.spec.json, tokens/sys/measure.tokens.json

组件负责的部分

  • 它提供进度条角色,以标签作为名称。确定形式下,它提供范围、当前值(限制在范围之内),以及数值文本(如果有的话);不确定形式下,它不提供数值,而提供平台的“忙碌”状态。Flutter 目前还没有进度条角色和忙碌状态,所以在那里它提供标签和数值。
  • 它从不可聚焦,也不会朗读自己的变化。用户到达它时,辅助技术会读出当前值。
  • 用户要求减弱动态效果时,数值的变化会立即显示;不确定的形式停止运动,改为轻缓地改变不透明度,每秒至多一次。
  • 在看不见它的时候,它的循环动画会停止——在所有平台上,应用处于后台时都是如此;在平台能够判断的情况下,它被滚出视野或位于隐藏的屏幕上时也是如此——看得见时再恢复。
  • 空的标签、超出范围的数值和未知的 separation 会在调试构建中报告。

需要你做的事

  • 任务的结束要由你自己来朗读:在实时区域(live region)里放一条消息,或者把焦点移到结果上。指示器不会说明任务已经完成或已经失败。
  • 如果你更改了颜色,请让指示器的颜色相对于轨道和它背后的表面至少保持 3:1 的对比度,使用分隔线和使用间隙时都是如此。数值是从指示器与轨道相接的位置读出来的。
  • 在最高容器色调的表面上(例如填充卡片),轨道几乎是同一色调,很难看清。指示器放在这样的表面上时,请在图层中把 comp.progress-indicator.track.color 指向更强的角色。指示器所显示的内容仍然从它与轨道相接的位置读出。
  • 如果任务会阻塞页面的某个区域,请在任务运行期间把这个区域标记为忙碌(在 Web 上是 aria-busy)。