跳到正文
misoto22 design

图表

WaterfallChart

一个总数是怎么从一个数走到另一个数的。

什么时候用它

「为什么变了」,而且贡献项可以是负的。饼图放不下负的扇形;各部分不必刚好补齐两个总数之间的差额时,用 BarChart。

示例

default

ARR bridge, FY24 to FY25Thousands of AUD. The steps are simultaneous; the order is editorial.
ARR bridge, FY24 to FY25
nameChangeRunning total
FY24 ARR4,2004,200
New business1,1805,380
Expansion6406,020
Downgrades-3105,710
Churn-8204,890
FY25 ARR04,890

subtotals

Operating profit bridge
Operating profit bridge
nameChangeRunning total
Revenue8,4008,400
COGS-3,1005,300
Gross profit05,300
Sales-1,4503,850
R&D-1,9001,950
G&A-7201,230
Operating profit01,230

说明

一个总量是怎么从一个数走到另一个数的——期初的余额、把它推动过去的那些带正负号的变动,以及最后收在哪里。

这是回答「为什么会变」的那种形式:两根柱子答不了,饼图答得是错的——因为变动可以是负的,而扇区不能。当各部分并不需要正好凑成两个总量之间的差额时,用 <BarChart>;当这个量只会越来越小时,用 <FunnelChart>

真正要当心的断言,是那些连接线。它们把各步画成了一个序列——先发生这个,再发生那个——而大多数拆解压根没有先后:同一个月里的流失和扩张是同时发生的,它们被列出来的顺序不过是一次编辑取舍。算术怎么排都成立,故事不成立,而读者一定会把最左边那根柱子当成第一个原因。还有两个连带的坑:中间那些柱子是悬空的长度,没有共同的基线可对,所以瀑布高处的一小步很难和接近零处的一大步比大小;而任何一步如果本身是两笔更大的反向变动净出来的,从图上根本看不出它是个净额。顺序是随手排的,就在 description 里说明白。

Recharts 在这里挣得的位置是坐标轴、网格和 tooltip。每根柱子都是一段悬空的区间——从当前累计值到新的累计值——上面再盖一个自定义形状,因为 Recharts 没有瀑布图这种标记,而连接线必须和它所连接的柱子出自同一套几何。

属性

WaterfallChart props
属性类型默认值说明
children必填ReactNode组合进来的各个部分——坐标轴、网格、tooltip,以及 `<WaterfallChart.Bars>`。
data必填WaterfallStep[]各步变动,按它们生效的顺序。顺序就是算术本身:这里不会替你排序,因为换一个顺序,每一根中间柱子上的累计值都变了。
title必填string这张图在讲什么,用一句读者能据此行动的话。必填,而且即使不显示出来也会播报给读屏软件。
chartPropsComponentProps<typeof RechartsBarChart>通往原始 Recharts 图表元素的逃生口。
classNamestring最后合并到 figure 上,所以调用处可以决定它的尺寸和间距。
configChartConfigDEFAULT_CONFIG唯一的那条序列——它的标签和上色。只会读**第一条**;瀑布图只有一个量,有多少步就有多少根柱子。
descriptionReactNode标题下面的一行——单位、时间窗口、需要提醒的地方。
emptyChartEmptyProps | false图表没东西可画时显示什么。传 `false` 保留坐标轴,用于那种「空本身就是结论」的图。
formatValue(value: number) => stringdefaultTick格式化图上印出来的每一个数字——刻度、标签、tooltip、表格。
hideDataTablebooleanfalse去掉隐藏的表格视图。只有当页面自己已经把数据印出来时,这才是对的。
showTitleboolean把标题印在图上方,而不是藏起来。

类型

TSX
export type WaterfallStepType = 'delta' | 'total'
export type WaterfallDirection = 'increase' | 'decrease' | 'total'

无障碍

  • 连接线是要小心对待的那个断言:它把各步画成一个**序列**,可绝大多数拆解根本不是序列——同一个月里的流失和扩张是同时发生的,而读者会把最左边那根柱子当成第一个原因。顺序是任意的时候,在 description 里说出来。
  • 中间那些柱子是悬空的长度,没有基线可读,所以级联上方一个小的变动很难和接近零处一个大的变动作比较。只有 total 柱坐在坐标轴上,也只有它们能被绝对地读出来。
  • 方向由标签的正负号和柱子的纹理承担,不只靠位置——所以在灰度和强制颜色下这个读法仍然成立。
  • 不给 value 的收尾柱由各个增减量算出来,把这道算术留在数据里,而不是留在调用者脑子里。