跳到主要内容
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.number-ticker
层级
基础

概览

Web
tanrei-number-ticker
clock_loader_40
SwiftUI
TanreiNumberTicker
radio_button_unchecked
Jetpack Compose
TanreiNumberTicker
radio_button_unchecked
Flutter
TanreiNumberTicker
radio_button_unchecked
React Native
NumberTicker
radio_button_unchecked

生成自 docs/status/status.json, specs/number-ticker.spec.json

进入视野时升到各自数值的数据

用法

Web 和 React 示例遵循当前规格。SwiftUI、Compose、Flutter 和 React Native 已在计划中,尚未提供数字滚动器。

<tanrei-number-ticker value="1234" label="订单数"></tanrei-number-ticker>
<tanrei-number-ticker value="98400" number-style="currency" currency="USD" label="收入"></tanrei-number-ticker>

<!-- 仪表板上的数据在第一次进入视野时从 0 升起。 -->
<tanrei-number-ticker start-value="0" value="0.426" number-style="percent" fraction-digits="1"></tanrei-number-ticker>

<!-- 用户正在改变的数:每次变化停下后只播报一次。 -->
<tanrei-number-ticker value="2" live="polite" label="购物车中的商品"></tanrei-number-ticker>

<script type="module">
  import '@tanrei/web/components/number-ticker';
  const ticker = document.querySelector('tanrei-number-ticker');
  ticker.value = orders.length;
  ticker.addEventListener('settle', (event) => console.log('now showing', event.detail.value));
</script>
import { NumberTicker } from '@tanrei/react';

<NumberTicker value={orders.length} label="订单数" />
<NumberTicker value={revenue} numberStyle="currency" currency="USD" label="收入" />
<NumberTicker startValue={0} value={0.426} numberStyle="percent" fractionDigits={1} />
<NumberTicker value={count} live="polite" label="购物车中的商品" onSettle={(event) => setShown(event.detail.value)} />

规格

动画:slide(默认系统的选择)、blur、blur-slide 和 flip

trend:auto(19 到 20 两位都向上)和 digit(个位从 9 向下到 0)

构成

部件 说明
1 number The formatted number, laid out as text in the style of where it stands: every digit in a column of its own, every other character (a sign, a separator, a currency, a unit, a compact suffix) as plain text between them.
2 digit One digit's place: a window one digit high showing one digit of a column of the digits 0 to 9 stacked from top to bottom, which moves through the window to show another.
3 text The label and the formatted value as one text for assistive technology, not drawn.

属性

属性 类型 默认值 说明
value number 0 The number shown.
style decimal | percent | currency | unit "decimal" How the number is written, by the platform's number formatter. percent multiplies by 100 and adds the locale's percent sign; currency writes it as an amount of currency; unit with the unit unit names.
currency string "" The currency while style is currency, as its three-letter code ("JPY", "EUR"). Not words: the formatter writes the locale's symbol.
unit string "" The unit while style is unit, as the formatter's identifier of a unit ("kilometer-per-hour", "megabyte"). Not words: the formatter writes the locale's form.
notation standard | compact "standard" standard writes every digit; compact writes the number short with the locale's suffix (1.2K, 1.2万).
fractionDigits number null How many digits after the decimal separator are always written. Null leaves it to the formatter for the style (none for a whole decimal, the currency's own for currency).
signDisplay auto | always | except-zero "auto" When a sign is written: auto for a negative number only; always for every number; except-zero for every number but zero (a change: +12, −3, 0).
locale string "" The language the number is written in, as a language tag ("ja-JP", "ar-EG"). Empty takes the language of where the ticker stands, and the platform's where none is set.
trend auto | up | down | digit "auto" Which way the digits move. auto: every changed digit moves the way the whole number moved — up when it grew, down when it shrank — so that 19 to 20 moves both digits up. up and down force one way. digit: each digit moves the way its own value moved, so that 19 to 20 moves the ones down from 9 to 0.
animation auto | slide | blur | blur-slide | flip | none "auto" How a digit changes. slide: its column moves through the window. blur: the old digit blurs out as the new one sharpens in. blur-slide: both, the new one coming from the side the digit moves from. flip: the upper half of the old digit turns down over the lower half, as a split-flap board. none: at once. auto takes comp.number-ticker.animation, the design system's answer (slide in the default system).
startValue number null A value to move from the first time the ticker comes into view (a dashboard's figures rising to their values). Null draws the first value at once.
duration number null How long a change takes, in milliseconds. Null takes comp.number-ticker.motion.duration.
live off | polite "off" Whether assistive technology is told each new value as it settles. off for a number that is not the point of the moment (most figures); polite for one a person waits for (a counter they are changing, a result arriving).
label string "" Words read before the value ("Revenue"), where nothing visible beside the number already says what it is. Never drawn.

事件

事件 说明
settle The digits came to rest at value: once at the end of each change's motion (at once where there is none). Carries value.

状态

行为

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

ID 需求
NTK-1 THE number ticker SHALL write value with the platform's number formatter in locale (the language of where it stands while locale is empty), with style, currency, unit, notation, fractionDigits and signDisplay, SHALL draw every digit of the result — in the locale's own digits — in a digit place of its own, in tabular figures of the font of where it stands so that every digit is as wide as every other, and every other character the formatter writes as plain text in that font; it SHALL lay the number out inline, in the text style, color and direction of where it stands, as one run isolated from the text around it, and SHALL draw no fill, edge or color of its own.
NTK-2 THE number ticker SHALL draw each digit place as a window one line high that shows one digit of a column of the locale's digits 0 to 9 stacked from top to bottom, clipped to the window, so that a greater digit lies lower in the column; to show a digit the column SHALL stand with that digit in the window.
NTK-3 WHEN value changes while animation is slide THE number ticker SHALL move the column of every digit place whose digit changes from the old digit to the new one: WHILE trend is digit, down the column toward a greater digit (the column moving up through the window, the new digit rising from below) and up it toward a lesser one; WHILE trend is auto, every changed digit in the direction the whole number moved — toward the greater digits when value grew and toward the lesser when it shrank — going on past 9 to 0 (or back past 0 to 9) where the new digit lies the other way, so that 19 to 20 moves the ones one step on from 9 to 0 and the tens one step on from 1 to 2; WHILE trend is up or down, every changed digit in that direction as auto does for a number that grew or shrank. Each column SHALL move over the change's duration (duration, or comp.number-ticker.motion.duration while it is null) with comp.number-ticker.motion.easing, each digit place, from the most significant to the least, starting comp.number-ticker.motion.stagger after the one before it.
NTK-4 WHEN value changes while animation is blur THE number ticker SHALL cross-fade each changed digit over the change's duration, the old digit blurring from none to comp.number-ticker.blur.radius as it fades out and the new one sharpening from that radius to none as it fades in; WHILE animation is blur-slide it SHALL do the same while moving the old digit out and the new one in by comp.number-ticker.blur-slide.distance in the direction slide would (NTK-3); WHILE animation is flip it SHALL turn the upper half of the old digit down about the digit's middle over the lower half, showing the upper half of the new digit behind it and then its lower half, as a split-flap board, over the change's duration with comp.number-ticker.motion.easing.
NTK-5 WHEN the formatted number gains or loses characters (99 to 100, 999 to 1,000, a sign that appears) THE number ticker SHALL move its width to the new one with the digits' motion, keeping its end edge still WHILE it is aligned to the end of where it stands and its start edge still otherwise; a digit place that appears SHALL slide in with its column from the side the change moves from and fade in, one that goes SHALL slide out the other way and fade out, and a character that is not a digit SHALL fade in or out in its place.
NTK-6 WHEN value changes again before a change's motion has ended THE number ticker SHALL move every digit place from where it is drawn at that moment to the new digit, by the rule of NTK-3 for the new change, and SHALL never jump to a digit first; the change's duration SHALL start again from that moment.
NTK-7 WHERE motion is reduced, or WHILE animation is none, THE number ticker SHALL show the new number at once, its width included — cross-fading the old number to the new one over comp.number-ticker.motion.reduced-duration where motion is reduced and the platform can fade — and SHALL emit settle at once.
NTK-8 WHEN the ticker first comes into view WHILE startValue is not null THE number ticker SHALL draw startValue and move to value as a change of NTK-3 and NTK-4, once; WHILE startValue is null, and on every later first drawing, it SHALL draw value at once without a motion.
NTK-9 THE number ticker SHALL expose to assistive technology one text — label where it is not empty, then the number written as NTK-1 says — and SHALL hide every drawn digit, column and character from it, so that the number is read once, as written, and never digit by digit or as the digits passing through the windows; the text SHALL be the current value, in place, read where the ticker stands — WHILE live is polite, the value of the last change that settled (NTK-10).
NTK-10 WHILE live is polite THE number ticker SHALL tell assistive technology the text of NTK-9, politely and without moving focus, once WHEN a change's motion ends — the text itself changing only then, so that what a region holds is what it says — and SHALL tell it no value in between, nor any value of a change that a later change replaced before it ended; WHILE live is off it SHALL tell it nothing by itself.
NTK-11 WHEN the digits come to rest at value THE number ticker SHALL emit settle once with value; a change that a later change replaces before its end SHALL emit none.
NTK-12 IF value is not a finite number THE number ticker SHALL draw what the formatter writes for it (the locale's sign for infinity, or its word for a value that is not a number) as plain text at once, without a motion, and in debug builds report it; IF style is currency and currency is not a code the formatter knows, or style is unit and unit is not a unit it knows, THE number ticker SHALL write the number as decimal and in debug builds report the value.
NTK-13 WHERE the layout direction is right-to-left THE number ticker SHALL lay out the number as the formatter writes it for its locale — digits in the order the locale writes them, which is left to right in every locale, isolated from the text around it — and SHALL move its columns up and down as in a left-to-right layout; only the side it stands at and the edge NTK-5 keeps still follow the direction.
NTK-14 THE number ticker SHALL take its layout direction and its language from where it is used — the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN either changes while the ticker is shown THE number ticker SHALL write and draw the number for the new one at once, without a motion.
NTK-15 WHILE the text size is enlarged THE number ticker SHALL grow with the text it stands in, its windows a line of the larger text high and its motion's distances with them.
NTK-16 WHEN animation names a value the active design system does not have, or trend, style, notation, signDisplay or live a value the component does not define, THE number ticker SHALL use the default value (auto, decimal, standard, auto, off) and in debug builds report the unknown value; WHEN duration is not a positive number, or fractionDigits not a whole number from 0 to 100, THE number ticker SHALL treat it as null and in debug builds report it.
NTK-17 THE number ticker SHALL have no words of its own and SHALL read none of the product's words: label is about this one number, and every sign, separator, symbol and suffix is the platform's formatter's, in the locale.

各平台名称对照

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

规格 Web SwiftUI Jetpack Compose Flutter React Native
组件 tanrei-number-ticker TanreiNumberTicker TanreiNumberTicker TanreiNumberTicker NumberTicker
value · 属性 value value value value value
style · 属性 numberStyle format format format format
currency · 属性 currency format format format format
unit · 属性 unit format format format format
notation · 属性 notation format format format format
fractionDigits · 属性 fractionDigits format format format format
signDisplay · 属性 signDisplay format format format format
locale · 属性 locale locale locale locale locale
trend · 属性 trend trend trend trend trend
animation · 属性 animation animation animation animation animation
startValue · 属性 startValue startValue startValue startValue startValue
duration · 属性 duration duration duration duration duration
live · 属性 live live live live live
label · 属性 label label label label label
settle · 事件 settle onSettle onSettle onSettle onSettle
备注 <tanrei-number-ticker value number-style currency unit notation fraction-digits sign-display locale trend animation start-value duration live label>; style is spelled numberStyle (attribute number-style) because style is every element's own. Intl.NumberFormat(…).formatToParts gives the characters; each digit place is an inline-block column moved by transform: translateY() through the Web Animations API, and a retarget reads the drawn position from getComputedStyle first (NTK-6). The width moves with the shared size transition primitive. The text of NTK-9 is a visually hidden span; the drawn number is aria-hidden. The first view of NTK-8 is an IntersectionObserver. The characters are laid out as flex items in the locale's direction, the digits and their separators in one left-to-right run (NTK-13). A change of an ancestor's lang is seen by one MutationObserver shared by every ticker (NTK-14). The six format props are one FloatingPointFormatStyle value (.number, .percent, .currency(code:), .measurement, with .notation(.compactName), .precision, .sign(strategy:)); locale is the environment's unless given. contentTransition(.numericText(countsDown:)) is not used: it cannot follow the trend rule of NTK-3 or the retarget of NTK-6. The six format props are one TanreiNumberFormat value built on ICU's NumberFormatter (android.icu.number). Each digit column is an Animatable offset; liveRegion = LiveRegionMode.Polite is set on a node whose text changes only at settle. The six format props are one NumberFormat of the intl package. Semantics(liveRegion:) on a node whose label changes only at settle. The six format props are one format: Intl.NumberFormatOptions (Hermes ships Intl.NumberFormat). accessibilityLiveRegion="polite" on Android and AccessibilityInfo.announceForAccessibility on iOS, at settle only.

令牌

令牌 引用 解析值(浅色)
--tanrei-comp-number-ticker-animation — 0
--tanrei-comp-number-ticker-blur-slide-distance sys.space.space100 8px
--tanrei-comp-number-ticker-blur-radius sys.space.space100 8px
--tanrei-comp-number-ticker-motion-duration sys.motion.role.resize.large 400ms
--tanrei-comp-number-ticker-motion-easing sys.motion.easing.spatial cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-number-ticker-motion-reduced-duration sys.motion.role.reduced 100ms
--tanrei-comp-number-ticker-motion-stagger sys.motion.duration.short1 50ms

测试样例

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

生成自 specs/number-ticker.spec.json, tokens/comp/number-ticker.tokens.json, design/tokens/

指南

何时使用

当一个数字在人观看时变化,而且变化本身值得被看到时,使用数字滚动器:随订单到来而增加的计数、变动的价格、分数、页面打开时升到各自数值的仪表板数据。动作表明数字变了、朝哪个方向、大约变了多少。

很少变化或没人看着它变化的数字,用普通文本即可。已知范围内的水平——例如配额中已用的存储——用计量条。时间和日期不是数字:请用平台的日期和时间格式书写。

数字如何书写

数字滚动器没有自己的文字,也没有自己的数字规则。平台的数字格式化器用读者的语言书写数字:分组符和小数点、货币符号、百分号、大数的简写(1.2K、1.2万),以及数字本身(埃及阿拉伯语写作 ١٢٣٤)。你可以选择样式(decimal、percent、currency 或 unit)、货币代码或单位、简写记法、固定的小数位数,以及何时写正负号;不指定语言时,它使用所在位置的语言。

它也没有自己的字体、字号和颜色:它以所在位置的文字样式、等宽的数字绘制,所以位数变化时数字不会左右移动。它既可以是仪表板卡片上的大数字,也可以是句子中的一个计数。

如何选择动画

动画 适用于
Slide 大多数数字。每个变化的位的数字列滚动到新数字。默认系统的选择。
Blur 更安静的变化:旧数字模糊淡出,新数字清晰浮现。
Blur slide 同上,并且数字朝变化的方向移动一小段。
Flip 翻页式显示板:发车信息、倒计时、外观偏机械的产品。
None 必须立即变化的数字。

在整个产品中只用一种动画:在设计系统中设定一次(comp.number-ticker.animation),而不是在每个滚动器上设定。

如何选择 trend

默认情况下,每个变化的位都朝整个数字变化的方向移动:增大时向上,减小时向下,所以 19 到 20 两位都向上。这读起来是一个数字的一次变化。当每一位本身都有含义时选择 digit——每一位按自己的变化移动,于是个位从 9 向下到 0。up 和 down 固定一个方向,适用于只朝一个方向计数的数字。

建议

  • 把它放在文本中,或给它的容器设定你想要的文字样式。它的一切都取自那里。
  • 当数字旁边没有可见的内容说明它是什么时,给它一个标签:“收入”“订单数”。
  • 对自行变化的数据保持 live 关闭。只有对用户正在等待或正在改变的数字才打开。
  • 在仪表板上使用起始值,让数据在第一次被看到时升起一次。

不建议

  • 不要通过滚动器把增加涂成绿色、把减少涂成红色:它没有这样的颜色。如果变化有含义,在数字旁边说明——标签、文字、带标签的箭头。
  • 不要每秒多次更新一个数字。每次变化都要花一个动作的时长;接连不断的变化看起来像噪声。按人能跟上的节奏更新。
  • 不要用它显示时间。时钟的位在 59 而不是 9 时进位。

无障碍

角色
none (text)
标注
Read as one text: label, then the number as the formatter writes it. The drawn digits are hidden. With live polite, the settled value is told once per change.
最小目标
即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px

键盘操作

按键 动作

备注

  • Not interactive: no focus, no input, no target; minimumTarget holds trivially.
  • Live regions: a number that is not the point of the moment is told nothing (off); one a person waits for is told politely once it settles, never each value it passes through.
  • Pause, Stop, Hide (WCAG 2.2.2): one change's motion ends well within five seconds. A number the product updates by itself, again and again, alongside other content is auto-updating content: the product gives the means to pause the updates, or to set how often they come.
  • Animation from interactions (WCAG 2.3.3): where motion is reduced, the number changes at once (NTK-7).

生成自 specs/number-ticker.spec.json, tokens/sys/measure.tokens.json

组件负责的部分

  • 辅助技术读取一段文本:标签,然后是按格式书写的数字。绘制出的数字被隐藏,所以数字不会被逐位朗读,也不会读出经过窗口的数字。
  • 默认不播报任何内容:大多数数据不是当下的重点。把 live 设为 polite 后,每次变化停下时礼貌地播报一次,从不播报经过的值,也不播报被后来的变化取代的值。
  • 在请求减少动态效果时,新数字立即出现,旧数字短暂淡出。
  • 它采用所在位置的语言和方向,任一变化时立即重新书写数字。
  • 未知的 animation、trend、style、notation、sign display 或 live 值、非正数的 duration、格式化器不认识的货币或单位,以及不是数字的值,会在调试版本中报告。

你需要做的

  • 如果你的产品在其他内容旁边一再自行更新一个数字,请提供暂停更新或选择更新频率的方式(WCAG 2.2.2 暂停、停止、隐藏)。
  • 在重要的地方用文字说明变化的含义。动作只表明数字变了,不说明这是好是坏。