news 2026/10/13 6:15:25

ECharts双轴组合图实战:柱状图与折线图混合配置及避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ECharts双轴组合图实战:柱状图与折线图混合配置及避坑指南

1. 为什么要在同一个图表里塞柱状图和折线图

做过数据看板的人大概率都遇到过这种需求:老板或者产品经理指着屏幕说,“这个销售额用柱子表示,增长率用线画出来,放一张图里,我要一眼看明白”。这不是炫技,这是真实业务场景里最高频的图表诉求之一。柱状图负责表达绝对量,比如每月营收、每日订单数、各渠道用户量;折线图负责表达趋势和比率,比如同比增长率、转化率、环比变化。两者叠加,读者既能看到“盘子有多大”,又能看到“跑得快不快”。

ECharts 作为国内数据可视化领域使用最广的图表库之一,天然支持这种组合图表。它的核心机制在于series数组里可以混合声明不同类型的系列,再配合双 Y 轴(yAxis数组)来解决量纲不一致的问题。听起来简单,但实际操作中坑不少:柱子和折线对不齐、双轴刻度错位、图例点击后布局塌陷、数据为空时折线断掉……这些问题官方文档不会逐一告诉你,只有真正在项目里踩过才知道。

这篇文章适合三类人看:第一类是对 ECharts 有基本了解、能画出单一柱状图或折线图,但没做过组合图的开发者;第二类是做过组合图但总觉得“哪里不对劲”,想搞清楚双轴对齐、数据缩放这些细节的人;第三类是需要在真实业务看板里快速落地、不想反复试错的人。我会从设计思路讲到参数配置,再到完整可运行的代码和排查经验,尽量让你看完就能直接抄到项目里用。

需要提前说明的是,下面涉及的具体参数值、配色方案、刻度策略,一部分来自官方文档的明确说明,另一部分是我在多个数据看板项目中总结出来的常见实践。不同业务场景下最优解可能不同,但底层逻辑是通用的。

2. 组合图表的核心设计思路拆解

2.1 单轴还是双轴:先想清楚量纲问题

很多人第一次做组合图,最容易犯的错误就是把柱状图和折线图塞进同一个 Y 轴。结果就是:如果柱子数值是几万,折线数值是百分之几,折线会被压成贴着 X 轴的一条直线,完全看不出趋势。反过来,如果折线数值大、柱子数值小,柱子就矮得看不见。

所以第一件事是判断两个系列的量纲是否一致。判断方法很简单:把两组数据的最大值和最小值分别列出来,如果数量级差距超过一个数量级(比如一组是千级、一组是百分数),那就必须用双 Y 轴。如果两组数据量纲接近,比如都是金额、都是人数,那单轴反而更直观,因为读者不需要在左右两个刻度之间来回换算。

双轴的本质是给两组数据各自一套独立的刻度尺。ECharts 里通过yAxis配置成数组来实现,第一个元素是左轴,第二个元素是右轴。每个 series 通过yAxisIndex指定自己绑定哪根轴。这个设计很灵活,但代价是:两根轴的刻度线如果不对齐,视觉上会非常别扭。后面我会专门讲怎么处理这个问题。

2.2 series 混合声明的底层逻辑

ECharts 的series是一个数组,每个元素代表一个数据系列。数组里元素的顺序决定了绘制层级:排在后面的系列会覆盖在前面的系列之上。这一点在组合图里特别重要——通常我们希望折线压在柱子上面,所以折线系列的type: 'line'应该放在柱状图type: 'bar'之后。

每个系列除了type之外,还有几个关键属性需要关注。name用于图例显示和 tooltip 标识;data是数据数组;yAxisIndex决定绑定哪根 Y 轴;itemStyle控制颜色和样式;smooth控制折线是否平滑。这些属性单独看都很简单,但组合在一起时,优先级和相互影响需要理清楚。比如yAxisIndex如果不写,默认是 0,也就是左轴,很多人忘了给折线指定yAxisIndex: 1,结果折线还是被压在左轴上,白折腾半天。

2.3 视觉层级与信息密度的平衡

一张图里同时有柱子和折线,信息密度天然就比单一图表高。如果不做视觉分层,读者会不知道先看什么。我的经验是:柱子用低饱和度的填充色,折线用高饱和度的线条色,并且折线加粗一点。这样视觉上柱子是“背景板”,折线是“主角”,符合“先看趋势、再看量”的阅读习惯。

另外,柱子的宽度和折线的点大小也要协调。柱子太宽会显得笨重,太窄又像牙签。折线的数据点标记(symbol)如果每个点都显示,在数据量大时会很乱,通常只在数据点较少(比如少于 12 个)时才显示标记,否则只保留线条。这些细节没有标准答案,但有一个原则:让读者在 3 秒内抓住你想表达的核心信息。

3. 核心配置项逐一拆解与实操要点

3.1 yAxis 双轴配置的关键参数

双 Y 轴是组合图的骨架。下面是一个典型的双轴配置结构:

yAxis: [ { type: 'value', name: '销售额(万元)', position: 'left', axisLabel: { formatter: '{value}' }, splitLine: { show: true, lineStyle: { type: 'dashed', color: '#e0e0e0' } } }, { type: 'value', name: '增长率', position: 'right', axisLabel: { formatter: '{value}%' }, splitLine: { show: false } } ]

这里有几个关键决策点。第一,position必须明确指定 left 和 right,否则两根轴可能重叠。第二,splitLine只在一根轴上显示,通常是左轴,因为两根轴的网格线如果都显示,会形成密集的横线,视觉干扰很大。第三,axisLabel.formatter要根据数据含义加上单位,百分比加%,金额加万或元,这样读者不用猜。

还有一个容易被忽略的点:两根轴的min和max如果不手动设置,ECharts 会自动计算,但自动计算的结果可能导致两根轴的零刻度不在同一水平线上。比如左轴从 0 开始,右轴从 -10 开始,那柱子的底部和折线的起点就会错位。解决办法是给两根轴都设置min: 0(如果数据都是正数),或者手动计算一个合理的范围。

3.2 series 中柱状图与折线图的属性差异

柱状图和折线图虽然都是系列,但它们的属性侧重点完全不同。柱状图关注的是barWidth、barGap、itemStyle.color;折线图关注的是smooth、symbol、lineStyle.width。

柱子的barWidth我一般设置为'40%'到'60%'之间。太宽会显得笨重,太窄在数据点多的时候会看不清。如果只有一组柱子,barWidth可以大一点;如果有多组柱子并列,就要小一点,给彼此留出间隙。barGap控制同类别下不同柱子之间的间距,默认是'30%',通常不需要改。

折线的smooth属性我建议设为true,除非数据本身波动非常剧烈、需要强调每一个拐点。平滑曲线在视觉上更柔和,适合表达趋势。但要注意,平滑曲线在数据点很少时可能产生“过冲”现象,也就是曲线在两点之间鼓出去,看起来像是数据超出了实际范围。如果数据点少于 5 个,建议关掉smooth。

symbol控制数据点的标记形状,默认是'circle'。如果数据点超过 15 个,建议设为'none',只保留线条,否则标记会挤成一团。lineStyle.width我一般设为 2 到 3,比默认的 2 稍粗一点,让折线在柱子背景上更突出。

3.3 tooltip 与 legend 的联动配置

组合图的 tooltip 需要同时显示柱子和折线的数值,并且要带上单位。ECharts 的tooltip.trigger设为'axis'时,会同时显示该轴上所有系列的数据。但默认的格式可能不够清晰,建议用formatter自定义:

tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: function(params) { let result = params[0].axisValue + '<br/>'; params.forEach(function(item) { let unit = item.seriesName === '增长率' ? '%' : '万元'; result += item.marker + item.seriesName + ':' + item.value + unit + '<br/>'; }); return result; } }

axisPointer.type设为'shadow'时,鼠标悬停会显示一个阴影带,同时覆盖柱子和折线,视觉上更统一。如果设为'line',则只显示一条竖线,适合折线为主的场景。

legend 的配置相对简单,但有一个坑:当图例被点击隐藏某个系列时,双轴的刻度不会自动调整。比如隐藏了柱子系列,左轴的最大值还是按柱子的数据算的,导致折线被压扁。解决办法是监听legendselectchanged事件,动态调整轴的max值。这个后面在排查技巧里会详细讲。

4. 完整实操流程与可复现代码

4.1 数据准备与结构设计

假设我们要做一个“月度销售与增长率”的看板,数据如下:

月份销售额(万元)增长率
1月32012.5%
2月280-8.3%
3月41018.7%
4月3906.2%
5月52015.4%
6月480-3.1%

数据结构上,X 轴用月份数组,两个系列各自用数值数组。注意增长率有负数,所以右轴的min不能设为 0,需要让 ECharts 自动计算或者手动设一个负值下限。

4.2 完整配置代码与逐段注释

下面是一份可以直接复制到 ECharts 在线编辑器或项目里运行的完整配置:

option = { // 标题配置 title: { text: '月度销售与增长率', subtext: '数据来源:模拟数据', left: 'center' }, // 提示框配置 tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: function(params) { let result = params[0].axisValue + '<br/>'; params.forEach(function(item) { let unit = item.seriesName === '增长率' ? '%' : '万元'; result += item.marker + item.seriesName + ':' + item.value + unit + '<br/>'; }); return result; } }, // 图例配置 legend: { data: ['销售额', '增长率'], top: 30 }, // 网格配置,留出空间给双轴名称 grid: { left: '10%', right: '10%', bottom: '10%', containLabel: true }, // X轴配置 xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'], axisTick: { alignWithLabel: true } }, // 双Y轴配置 yAxis: [ { type: 'value', name: '销售额(万元)', position: 'left', min: 0, axisLabel: { formatter: '{value}' }, splitLine: { show: true, lineStyle: { type: 'dashed', color: '#e0e0e0' } } }, { type: 'value', name: '增长率', position: 'right', axisLabel: { formatter: '{value}%' }, splitLine: { show: false } } ], // 系列配置 series: [ { name: '销售额', type: 'bar', yAxisIndex: 0, data: [320, 280, 410, 390, 520, 480], barWidth: '45%', itemStyle: { color: '#7cb5ec', borderRadius: [4, 4, 0, 0] } }, { name: '增长率', type: 'line', yAxisIndex: 1, data: [12.5, -8.3, 18.7, 6.2, 15.4, -3.1], smooth: true, symbol: 'circle', symbolSize: 8, lineStyle: { width: 3, color: '#f7a35c' }, itemStyle: { color: '#f7a35c' } } ] };

这段代码里,我特意给柱子加了borderRadius: [4, 4, 0, 0],让柱子顶部有圆角,视觉上更现代。折线的symbolSize设为 8,比默认的 4 大一点,在柱子背景上更显眼。这些细节看似微小,但实际效果差别很大。

4.3 参数计算:如何让双轴刻度对齐

双轴刻度对齐是组合图里最考验经验的地方。ECharts 默认会为每根轴独立计算刻度,导致两根轴的网格线不在同一水平线上。虽然splitLine只显示一根轴的,但读者会下意识地用左轴的刻度去估算右轴的值,如果不对齐就会产生误读。

一个实用的技巧是:手动设置两根轴的interval,让它们的刻度数量一致。比如左轴范围是 0 到 600,分成 6 段,每段 100;右轴范围是 -10 到 20,分成 6 段,每段 5。这样两根轴的刻度线就能大致对齐。

计算方法是:先确定左轴的最大值maxLeft和分段数n,则intervalLeft = maxLeft / n。右轴同理。分段数一般取 4 到 6 之间,太少显得粗糙,太多显得密集。如果数据范围不规整,可以手动把max向上取整到一个方便的数,比如 520 取 600,-8.3 取 -10。

yAxis: [ { type: 'value', name: '销售额(万元)', min: 0, max: 600, interval: 100, // ...其他配置 }, { type: 'value', name: '增长率', min: -10, max: 20, interval: 5, // ...其他配置 } ]

这样配置后,两根轴各有 6 个刻度区间,网格线在视觉上是对齐的。实测下来,这种手动对齐的方式比完全依赖自动计算要稳定得多,尤其是在数据动态变化的看板里。

5. 常见问题与排查技巧实录

5.1 折线被压扁或柱子看不见

这是最常见的问题,根本原因通常是yAxisIndex没指定,或者双轴的范围设置不合理。排查步骤:

  1. 检查折线系列的yAxisIndex是否为 1,柱状图是否为 0。
  2. 检查右轴的min和max是否覆盖了折线数据的范围。如果折线数据有负数,右轴min不能是 0。
  3. 如果柱子看不见,检查左轴的max是否远大于柱子数据,导致柱子相对太矮。

下面这张速查表可以帮你快速定位:

现象可能原因解决方法
折线贴底折线绑定了左轴设置yAxisIndex: 1
柱子太矮左轴 max 过大调整左轴 max 或改用双轴
折线超出顶部右轴 max 过小增大右轴 max 或设为自动
双轴刻度错位未手动设置 interval手动设置两根轴的 interval
图例隐藏后布局塌陷轴范围未动态调整监听 legendselectchanged 事件

5.2 数据为空或全为 null 时的显示异常

真实项目里,数据接口返回空数组或者全是 null 的情况很常见。ECharts 默认会把 null 当作断点,折线会断开,柱子会消失。如果希望 null 显示为 0,需要在数据预处理阶段把 null 替换成 0。如果希望折线断开而不是连起来,可以设置connectNulls: false(默认就是 false)。

但有一个坑:如果整个系列的数据都是 null,ECharts 可能会报错或者不渲染。解决办法是在渲染前判断,如果数据全为空,就给一个空数组或者显示“暂无数据”的提示层。这个逻辑最好在业务代码里处理,不要指望 ECharts 自动兜底。

5.3 图例点击后的动态轴调整

前面提到,隐藏某个系列后轴范围不会自动调整。解决方法是监听legendselectchanged事件:

myChart.on('legendselectchanged', function(params) { let selected = params.selected; let newMax = 0; if (selected['销售额']) { newMax = Math.max(...salesData); } myChart.setOption({ yAxis: [ { max: newMax > 0 ? Math.ceil(newMax * 1.2) : 100 }, {} ] }); });

这段代码的逻辑是:如果销售额系列被隐藏,就把左轴的最大值设为一个默认值;如果显示,就按数据最大值的 1.2 倍来设置,留出一点顶部空间。实测下来,这个交互细节能显著提升看板的专业感。

5.4 移动端适配的注意事项

在移动端,双轴图表的空间非常紧张。左右两根轴的名称和刻度会占用大量横向空间,导致绘图区域被压缩。我的经验是:移动端优先考虑单轴方案,如果必须双轴,就把轴名称去掉,只保留刻度,并且把刻度字体调小。另外,grid的left和right要适当减小,给绘图区留出更多空间。

还有一个细节:移动端的 tooltip 触发方式建议用trigger: 'axis'配合axisPointer.type: 'line',因为手指点击不如鼠标精确,阴影带在移动端容易误触。

6. 进阶玩法与性能优化建议

6.1 多组柱状图叠加折线的布局策略

有时候业务需要展示多组柱子,比如“线上销售额”和“线下销售额”两组柱子,再加一条“总增长率”折线。这时候柱子的barWidth要调小,barGap也要调整,否则两组柱子会挤在一起。折线的yAxisIndex依然指向右轴,但要注意折线的数据点位置默认是在类目中间,如果两组柱子并列,折线点可能会偏左或偏右。可以通过xAxis的boundaryGap和系列的barGap配合微调。

6.2 大数据量下的渲染性能

当数据点超过 500 个时,柱状图和折线图的渲染会明显变慢。优化手段包括:关闭动画(animation: false)、减少symbol显示、使用large: true开启大数据模式(柱状图支持)、对数据进行采样或聚合。如果数据量真的很大,建议改用dataZoom让用户自己选择查看范围,而不是一次性渲染全部数据。

6.3 主题与配色的统一管理

组合图的配色最忌讳柱子和折线颜色太接近,或者对比度过高显得刺眼。我的做法是:柱子用冷色调的低饱和度色,折线用暖色调的高饱和度色,形成冷暖对比。如果项目有统一的设计规范,就把颜色提取成变量,方便全局替换。ECharts 支持通过theme注册全局主题,适合多图表统一风格的场景。

7. 我在实际项目里踩过的坑

第一个坑是双轴的零刻度不对齐。早期做的一个看板,左轴从 0 开始,右轴自动计算后从 -5 开始,结果柱子的底部和折线的起点差了半格,视觉上非常别扭。后来养成了习惯:只要用双轴,就手动设置min,确保两根轴的零刻度在同一水平线上。

第二个坑是图例顺序和绘制顺序不一致。ECharts 的图例顺序默认跟series数组顺序一致,但绘制层级是后面的覆盖前面的。如果想让折线在图例里排在柱子前面,但绘制时又要在柱子上面,就需要调整legend.data的顺序,而不是series的顺序。这个细节官方文档没有明说,是我调试了半天才发现的。

第三个坑是数据更新时的动画抖动。看板数据定时刷新时,如果直接setOption全量更新,柱子和折线会重新播放动画,看起来像在闪。解决办法是用setOption的第二个参数notMerge: false,并且只更新series.data,不更新整个配置。这样 ECharts 会做增量更新,动画更平滑。

最后分享一个小技巧:如果折线的数据点正好落在柱子的顶部,可以把折线的z值设高一点(比如z: 10),确保折线绘制在柱子之上。虽然默认情况下折线就在柱子上面,但在某些复杂配置下可能会被覆盖,显式设置z值更保险。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 6:14:06

Windows下CGNS静态库配置:HDF5/zlib/szip依赖与VS链接实战

简介&#xff1a;在 Windows 64 位环境下可直接使用的 CGNS 静态库集成包&#xff0c;面向从事流体力学、CFD 模拟或科学计算后处理的 C/C 开发者。CGNS 支持复杂三维、多时间步网格数据交换&#xff0c;本包已预先整合 HDF5、ZLIB、SZIP 依赖&#xff0c;免去 CMake 编译配置与…

作者头像 李华