Recharts 图表被极值压扁看不清?用 domain 和 allowDataOverflow 聚焦可视范围
【免费下载链接】rechartsRedefined chart library built with React and D3项目地址: https://gitcode.com/GitHub_Trending/re/recharts
当数据集里同时存在数量级悬殊的值时,Recharts 图表会出现一种典型问题:最大的那个值把坐标轴撑满,其余数据被压成不足 1px 的条形,肉眼完全看不见,只剩悬停 tooltip 才能读到数值。Recharts 官方文档用一个"太阳系质量柱状图"的例子完整演示了这个现象和修复路径:太阳质量高达 1.989e30 kg,而其他天体在 1e22 到 1e27 kg 之间,默认渲染下只能看到太阳一根柱子。本文按文档中的操作顺序,说明如何用domain和allowDataOverflow把可视范围聚焦到你关心的区间,以及为什么只加domain不够、值差距仍然过大时的替代手段。
先看被压扁的默认效果
Recharts 仓库中的示例 MassBarChart.tsx 展示了这个问题的起点。核心部分如下(数据为示例自带的太阳系质量数据,完整文件可直接复用):
import { Bar, BarChart, Tooltip, XAxis, YAxis } from 'recharts'; const solarSystem = [ { name: 'Sun', massKg: 1.989e30 }, { name: 'Mercury', massKg: 3.3e23 }, { name: 'Venus', massKg: 4.87e24 }, { name: 'Earth', massKg: 5.97e24 }, { name: 'Mars', massKg: 6.42e23 }, { name: 'Jupiter', massKg: 1.9e27 }, { name: 'Saturn', massKg: 5.68e26 }, { name: 'Uranus', massKg: 8.68e25 }, { name: 'Neptune', massKg: 1.02e26 }, { name: 'Pluto', massKg: 1.3e22 }, ]; export default function MassBarChart() { return ( <BarChart style={{ width: '100%', maxWidth: '800px', maxHeight: '80vh', aspectRatio: 1.618, }} responsive data={solarSystem} > <XAxis dataKey="name" /> <YAxis width="auto" label={{ value: 'Mass [kg]', position: 'insideLeft', dx: 0, dy: 20, angle: -90 }} /> <Bar dataKey="massKg" unit="kg" /> <Tooltip /> </BarChart> ); }仓库原示例中还引入了RechartsDevtools(来自@recharts/devtools,用于开发调试),上面已省略。
默认渲染下可以观察到两点:
- XAxis 是 category(分类)轴,把值当作字符串处理,不会尝试取"中间值";YAxis 是 number(数值)轴,把数据当作连续谱处理。
- Recharts 的 auto 行为会自动把刻度撑到"好看"的范围——最大质量是太阳的 1.989e30 kg,但 YAxis 会画出 2e30 的刻度。结果就是文档描述的原话:"Technically the data is there and you can mouse over it and see it in the tooltip, but the bars are less than 1px tall and so are invisible."
也就是说,数据没有丢,只是坐标轴范围被极值主导了。
为什么只加 domain 不生效
直觉上的修复是直接指定 YAxis 的 domain。但 Recharts 的官方指南 DomainAndTicks 里明确记录了一个坑:
Let's start by adding a domain prop like this:
<YAxis domain={[1e23, 1e25]} />. You will see that nothing has changed! That's because by default, Recharts will expand the domain so that it fits all the data.
只写domain时,Recharts 默认会把 domain 向外扩展到包含全部数据,所以太阳的极值仍然把可视范围撑满,图表看起来没有任何变化。
加 allowDataOverflow 把极值裁掉
关闭"自动扩展"的方式是在同一轴上加allowDataOverflow:
<YAxis domain={[1e23, 1e25]} allowDataOverflow />仓库中的完整示例是 MassBarChartCustomYDomain.tsx,其中 YAxis 配置为:
<YAxis domain={[1e23, 2e27]} allowDataOverflow width={100} label={{ value: 'Mass [kg]', position: 'insideLeft', dx: 0, dy: 20, angle: -90 }} />这里的domain={[1e23, 2e27]}是示例数据下的取值,读者应按自己的数据区间替换上下界。加上allowDataOverflow后的效果,文档描述为:"it will cut off the Sun bar (as it's 'off the chart') and it will allow us to see the individual planets mass"——太阳的柱形被裁掉(因为它"出了图"),各行星的质量条形因此可见。
这就是最小可用的主路径:先给数值轴写domain,再写allowDataOverflow,两个必须同时出现,只写domain会被默认扩展行为抵消。
domain 支持哪些写法
YAxis的domain属性在源码 src/cartesian/YAxis.tsx 的 JSDoc 中有完整说明:长度必须为 2,每个元素可以是:
- 数字:
[0, 100] - 字符串
'auto'、'dataMin'、'dataMax' - 可计算的字符串,如
'dataMin - 20'、'dataMax + 1000' - 接收单个值并返回数字的函数,例如
dataMin => 0 - Math.abs(dataMin) - 接收
[dataMin, dataMax]并返回[min, max]的函数,例如:
<YAxis type="number" domain={([dataMin, dataMax]) => { const absMax = Math.max(Math.abs(dataMin), Math.abs(dataMax)); return [-absMax, absMax]; }} />其中任意一个元素设为'auto'时,Recharts 会计算"漂亮"的刻度,最终 domain 由刻度生成。allowDataOverflow则与domain={[0, 100]}这类显式数值范围配合使用,即上面 JSDoc 中的示例domain={[0, 100]} allowDataOverflow。
验证:如何判断修复生效
文档给出的判断方式是直接对照渲染结果,而不是数值断言:
- 默认状态:只看得见太阳一根柱,其他条形不足 1px;
- 加上
domain+allowDataOverflow后:超出 domain 的柱形(示例中是太阳)被截断,落在 domain 内的行星质量条形变得可见。
如果验证后"岩石行星仍然小到看不见"(示例数据中确实如此,因为 1e23 到 1e27 之间差距仍有四个数量级),说明线性比例尺本身不适合对比数量级悬殊的值,这时文档给出的下一步是换刻度。
可选分支:log 刻度与自定义 ticks
用 log scale 对比数量级
默认刻度是 linear,"easy to read but it doesn't allow comparing very small and very large values. Logarithmic scale is better for that purpose (but it does require the reader to be careful)"。通过scale属性切换:
<YAxis scale="log" domain={[1e22, 3e30]} width={100} label={{ value: 'Mass log10[kg]', position: 'insideLeft', dx: 0, dy: 20, angle: -90 }} />这是仓库示例 MassBarChartLogScale.tsx 的写法,domain 覆盖 1e22 到 3e30,全部天体都能落入同一坐标轴。两点必须注意:
- 文档明确提示:"Note that the log scale has a bug where it requires one to set an explicit domain." 也就是说
scale="log"时必须显式给 domain,不能依赖 auto; scale也可以写成 d3-scale 的完整定义(scale属性支持字符串快捷方式或完整的 scale 定义对象,见 src/cartesian/YAxis.tsx 中scale的 JSDoc)。
用 tickFormatter 改刻度标签
如果不想让刻度显示科学计数法,可以用tickFormatter自定义刻度文本。仓库中的示例 MassBarChartCustomTicks.tsx 把质量值换算成 "yottagrams" 单位展示。这一步只影响刻度显示,不改变坐标轴范围。
边界与替代路径
- 换轴类型不是等价方案:指南里演示过把 YAxis 改成
type="category",结果是"shows more stuff",但每个数值被当作独立字面值处理,刻度不排序、不取整,且太阳条形高度为零(1.989e30 的刻度落在最底部起点)。文档的结论是"Recharts can do better",即domain+allowDataOverflow仍是推荐路径。 - 把 XAxis 改成
type="number"没有意义:示例中 X 轴是行星名,非数值数据转换后无内容可显示。 - 被裁掉的极值数据并未丢失:超出 domain 的值仍然在数据中,tooltip 可以读到(见 DomainAndTicks 指南),只是柱形在图外被截断。如果业务上必须让读者看到极值本身,需要另行取舍(比如单独标注),文档未给出更多方案。
完整的指南原文在 www/src/components/GuideView/DomainAndTicks/index.tsx,五个示例组件(基线、category Y 轴、自定义 domain、log scale、自定义 ticks)都在 DomainAndTicks 目录 下,可以直接对照运行。
【免费下载链接】rechartsRedefined chart library built with React and D3项目地址: https://gitcode.com/GitHub_Trending/re/recharts
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考