数据
BulletChart
一个度量、它的目标,以及说明这个数好不好的几条背景带。
什么时候用它
一页要盯十个数的状态页。Stephen Few 设计它就是为了替掉仪表盘上的表盘——那种东西花掉整张卡片,只把一个数说得很差。
示例
default
Availability96 / 99
Support CSAT84 / 80
Onboarding completion61 / 75
Docs coverage43 / 70
| name | Value | Target | Range bounds |
|---|---|---|---|
| Availability | 96 | 99 | 50, 80 |
| Support CSAT | 84 | 80 | 50, 80 |
| Onboarding completion | 61 | 75 | 50, 80 |
| Docs coverage | 43 | 70 | 50, 80 |
per measure scales
Uptime99.4 / 99.9
p95 latency310 / 250
Error budget left38 / 50
Open incidents3 / 0
| name | Value | Target | Range bounds |
|---|---|---|---|
| Uptime | 99.4 | 99.9 | 99, 99.5 |
| p95 latency | 310 | 250 | 200, 400 |
| Error budget left | 38 | 50 | 25, 60 |
| Open incidents | 3 | 0 | 1, 4 |
说明
一个度量、它的目标值,以及说明这个数到底好不好的区间带——每一个被追踪的东西占密密的一行。
Stephen Few 设计它,就是为了取代仪表盘上那个表盘,而这个理由至今站得住:一个速度表要花掉整整一张卡片,才把一个数字表达得很糟;子弹图则在一行文字的高度里,把同一个数字、它的目标值,以及围绕它的定性背景一并说清楚。十个叠起来,就是一页读者一眼扫得完的状态页。
它不做的事,是呈现随时间的变化。子弹图只是一个瞬间,target 是它唯一带着的对照;当问题是「我们是怎么走到这一步的」,它要的是 <LineChart>;当几个度量需要互相比较、而不是各自跟自己的目标比时,<BarList> 会替你排出名次,而这里不会。
真正要当心的是那些区间带。它们是一种判断——是某个人决定了 80 算「好」——却和测量值用同一种笔墨画出来,读者根本分不清哪个是达成共识的阈值、哪个是谁随手敲进去的数。它们还会压缩信息:一个接近刻度顶端的值,无论是压线过关一分还是甩开三十分,都落在同一条带里。区间带回答的是「这个能不能接受」,永远不是「差了多少」。
刻意不带渲染引擎——这是整套里唯一一张什么都不用算的图。每一行都只是一把线性刻度,没有坐标轴、没有刻度线、也没有共享的绘图区,所以它是用 HTML 排出来的:区间带和条形都是内联轴方向上的偏移量,这意味着整张图在从右往左的文档里能正确镜像,而一张画在用户坐标系里的 SVG 做不到。
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data必填 | BulletMeasure[] | 一个度量一条。顺序就是阅读顺序;这里不会替你排序。 | |
| title必填 | string | 这张图在讲什么,用一句读者能据此行动的话。必填,而且即使不显示出来也会播报给读屏软件。 | |
| className | string | 最后合并到 figure 上,所以调用处可以决定它的尺寸和间距。 | |
| description | ReactNode | 标题下面的一行——单位、时间窗口、需要提醒的地方。 | |
| domain | [number, number] | 各个度量默认沿用的刻度,写成 `[min, max]`。 | |
| empty | ChartEmptyProps | 图表没东西可画时显示什么。 | |
| formatValue | (value: number) => string | defaultTick | 格式化这张图印出来的每一个数字。 |
| hideDataTable | boolean | false | 去掉隐藏的表格视图。只有当页面自己已经把数据印出来时,这才是对的。 |
| ranges | number[] | 各个度量默认沿用的区间带,写成一串递增的上界。 只有当这些度量共用一把刻度时,共享区间带才有意义。刻度不同的时候——一个延迟和一个转化率并排——就把 `ranges` 和 `domain` 分别写到每个度量上。 | |
| showScale | boolean | false | 把刻度的两端印在每条轨道下面。 |
| showTitle | boolean | 把标题印在各行上方,而不是藏起来。 |
无障碍
- 纯 HTML + 逻辑属性写成,不需要渲染引擎,可服务端渲染,在从右到左的文档里也是对的。recharts 不在场时照样能用。
- 几条带子是一个**判断**,却和被测量的那个数用同一种墨画出来,所以页面必须交代它们从哪来。只是把量程三等分的带子会让图看起来「已经被评估过」,其实并没有。
- 它呈现的是一个瞬间,除了 target 之外不携带任何比较。「我们是怎么走到这儿的」要的是 LineChart。
- 共用的带子只有在几个度量共用同一把尺时才有意义——延迟和转化率并排时,要给每个度量各自的 ranges 和 domain。