echarts图表设计从入门到精通 常见柱状图折线图饼图选错类型导致数据误读 实战案例帮你避开这些坑
上周我们团队开了个数据复盘会,会上展示了一张销售趋势图,本来想说明Q4季度业绩下滑了,结果老板看了一眼就问:”你这折线怎么是往上走的?下滑应该往下才对。” 我当时就愣住了,低头一看,好家伙,我把纵坐标轴的方向搞反了。这张图如果发给客户,人家准得以为我们在庆祝增长,其实我们是在赔钱。
这种尴尬,几乎每个做过数据可视化的人都遇到过。今天我就把这些踩过的坑、掉过的坑,全都掏出来跟大家聊聊,顺便把正确的做法也讲清楚。
一、柱状图和折线图的”兄弟误会”
柱状图和折线图长得挺像,都是直直线条,但它们的性格完全不同。柱状图适合比较不同类别之间的数值,而折线图适合展示数据随时间变化的趋势。很多人一开始分不清,直接糊弄着用,结果闹出笑话。
坑1:用柱状图展示时间序列趋势——读者看不出”趋势”
想象一下,你手里有过去12个月的销售数据,想用柱状图来展示。乍一看,每个月的柱子高低确实能看出差异,但如果你想让人一眼就看出”从3月开始持续增长”,柱状图就不太合适了。因为柱子之间是断开的,视觉上没有连贯性,大脑需要额外费力去连接这些独立的柱子,才能感知到趋势。
错误示例代码:
// ❌ 错误:用柱状图展示月度趋势,读者难以感知连续变化
option = {
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: { type: 'value' },
series: [{
data: [820, 932, 901, 934, 1290, 1330, 1320, 1400, 1450, 1520, 1600, 1750],
type: 'bar', // 柱状图展示时间趋势,不够直观
itemStyle: { color: '#5470c6' }
}]
};
正确做法:用折线图展示趋势
// ✅ 正确:用折线图展示时间序列,趋势一目了然
option = {
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: { type: 'value' },
series: [{
data: [820, 932, 901, 934, 1290, 1330, 1320, 1400, 1450, 1520, 1600, 1750],
type: 'line', // 折线图,趋势清晰
smooth: true, // 平滑曲线,更易读
lineStyle: { width: 3 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(84, 112, 198, 0.5)' },
{ offset: 1, color: 'rgba(84, 112, 198, 0.05)' }
])
}
}]
};
坑2:用折线图比较类别数值——读者会误以为有”连续性”
反过来,如果你有一组不同产品类别的销售数据,比如”手机”“电脑”“平板”“耳机”,用折线图连接这些点,会给读者一种错误的暗示:这些数据点是有关联的、连续的。但实际上,手机和电脑之间没有任何时间或逻辑上的连续性。
错误示例代码:
// ❌ 错误:用折线图比较不同类别,暗示不存在的连续性
option = {
xAxis: {
type: 'category',
data: ['手机', '电脑', '平板', '耳机', '智能手表']
},
yAxis: { type: 'value' },
series: [{
data: [5200, 4300, 2100, 1800, 950],
type: 'line', // 类别数据用折线,会误导读者
connectNulls: true,
symbol: 'circle',
symbolSize: 8
}]
};
正确做法:用柱状图比较类别
// ✅ 正确:用柱状图比较不同类别的数值
option = {
xAxis: {
type: 'category',
data: ['手机', '电脑', '平板', '耳机', '智能手表']
},
yAxis: { type: 'value' },
series: [{
data: [5200, 4300, 2100, 1800, 950],
type: 'bar', // 柱状图,类别比较清晰
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#91cc75' },
{ offset: 1, color: '#57b757' }
]),
borderRadius: [6, 6, 0, 0]
},
label: {
show: true,
position: 'top',
formatter: '{c} 台'
}
}]
};
二、饼图的”隐形陷阱”
饼图是视觉上最容易产生误读的图表类型,因为它涉及角度和面积的感知。人眼对长度的判断比对面积和角度的判断要准确得多,所以饼图天然就容易让人看错。
坑3:饼图切片超过5个——读者根本分不清楚
我见过有人拿饼图展示一个公司有12个部门的预算占比,切出来的饼像彩虹糖一样花花绿绿。这种图我看了五遍,还是不知道红色那块到底代表哪个部门。
一个通用的建议:切片数量不要超过5-7个。 超过这个数量,要么合并小众类别为”其他”,要么换用柱状图。
错误示例代码:
// ❌ 错误:切片过多,难以辨识
option = {
series: [{
type: 'pie',
radius: ['30%', '70%'],
data: [
{ value: 1048, name: '搜索引擎' },
{ value: 735, name: '直接访问' },
{ value: 580, name: '邮件营销' },
{ value: 484, name: '联盟广告' },
{ value: 300, name: '视频广告' },
{ value: 280, name: '百度' },
{ value: 235, name: '谷歌' },
{ value: 210, name: '必应' },
{ value: 180, name: '搜狗' },
{ value: 150, name: '其他' }
],
label: { show: false }, // 标签太多, crowded
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
};
正确做法:合并小众类别,控制切片数量
// ✅ 正确:合并为6个切片,清晰可读
option = {
series: [{
type: 'pie',
radius: ['35%', '75%'],
center: ['50%', '50%'],
data: [
{ value: 1048, name: '搜索引擎', itemStyle: { color: '#5470c6' } },
{ value: 735, name: '直接访问', itemStyle: { color: '#91cc75' } },
{ value: 580, name: '邮件营销', itemStyle: { color: '#fac858' } },
{ value: 484, name: '联盟广告', itemStyle: { color: '#ee6666' } },
{ value: 814, name: '视频/搜索引擎其他', itemStyle: { color: '#73c0de' } },
{ value: 310, name: '其他', itemStyle: { color: '#3ba272' } }
],
label: {
show: true,
formatter: '{b}\n{c} ({d}%)', // 显示名称、数值和百分比
fontSize: 12
},
legend: {
orient: 'vertical',
right: '5%',
top: 'center',
textStyle: { fontSize: 13 }
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
};
坑4:饼图展示零值或负值——完全不可行
饼图的本质是”整体分成若干部分”,每一块都是整体的一个子集。这意味着每一块都必须是正数,而且加起来等于整体。如果你拿饼图去展示负增长或者零值数据,它完全站不住脚。
错误示例代码:
// ❌ 错误:饼图不能展示负数,会直接报错或显示异常
option = {
series: [{
type: 'pie',
data: [
{ value: 100, name: '产品A' },
{ value: -20, name: '产品B' }, // 负数!饼图无法处理
{ value: 0, name: '产品C' } // 零值!也没有意义
]
}]
};
正确做法:用柱状图展示正负数据
// ✅ 正确:正负数据用柱状图,一目了然
option = {
xAxis: { type: 'category', data: ['产品A', '产品B', '产品C', '产品D', '产品E'] },
yAxis: { type: 'value' },
series: [{
data: [100, -20, 0, 80, -45],
type: 'bar',
itemStyle: {
color: function(params) {
return params.value > 0 ? '#5470c6' : '#ee6666';
}
},
label: {
show: true,
position: 'inside',
formatter: function(params) {
return params.value > 0 ? '+' + params.value : params.value;
}
}
}]
};
坑5:3D饼图——花哨但误导
3D饼图看起来很有科技感,但实际上它会扭曲数据的视觉权重。靠近观察者的切片看起来更大,远离观察者的切片看起来更小。这种透视变形会让人对比例产生误判。我曾经在一份报告里用3D饼图展示市场份额,结果客户说”原来A公司份额这么小”,实际上A公司只比B公司少了5个百分点,但3D效果让A看起来只有B的一半大。
三、堆叠柱状图 vs 分组柱状图的”选择困难”
堆叠柱状图和分组柱状图看起来差不多,都是多组柱子并排或叠加,但它们的表达意图完全不同。选错了,读者看到的”故事”就完全不一样。
坑6:该用分组却用了堆叠——总和意义被放大
堆叠柱状图的每一根柱子代表一个总和,各段代表组成部分。如果你想表达的是”各个类别各自的数值对比”,用堆叠图会让读者关注总量而不是各个类别,造成误读。
错误示例代码:
// ❌ 错误:想比较各产品在各季度的销量,却用了堆叠图
option = {
xAxis: { type: 'category', data: ['Q1', 'Q2', 'Q3', 'Q4'] },
yAxis: { type: 'value' },
legend: { data: ['手机', '电脑', '平板'] },
series: [
{
name: '手机',
type: 'bar',
stack: 'total', // 堆叠!读者看到的是季度总销量,不是手机销量
data: [320, 332, 301, 334]
},
{
name: '电脑',
type: 'bar',
stack: 'total', // 堆叠!
data: [120, 132, 101, 134]
},
{
name: '平板',
type: 'bar',
stack: 'total', // 堆叠!
data: [220, 182, 191, 234]
}
]
};
正确做法:用分组柱状图比较各维度
// ✅ 正确:分组柱状图,各产品销量可直接对比
option = {
xAxis: { type: 'category', data: ['Q1', 'Q2', 'Q3', 'Q4'] },
yAxis: { type: 'value' },
legend: { data: ['手机', '电脑', '平板'] },
series: [
{
name: '手机',
type: 'bar',
// 不设置 stack,默认为分组
data: [320, 332, 301, 334],
itemStyle: { color: '#5470c6', borderRadius: [4, 4, 0, 0] }
},
{
name: '电脑',
type: 'bar',
data: [120, 132, 101, 134],
itemStyle: { color: '#91cc75', borderRadius: [4, 4, 0, 0] }
},
{
name: '平板',
type: 'bar',
data: [220, 182, 191, 234],
itemStyle: { color: '#fac858', borderRadius: [4, 4, 0, 0] }
}
]
};
坑7:该用堆叠却用了分组——组成部分的关系被忽略
反过来,如果你是想展示”总销量是多少,其中各产品贡献了多少”,那就应该用堆叠图。分组图会让读者只能比较单个产品的数值,看不到整体的构成关系。
// ✅ 正确:展示各季度总销量及构成,用堆叠图
option = {
xAxis: { type: 'category', data: ['Q1', 'Q2', 'Q3', 'Q4'] },
yAxis: { type: 'value' },
legend: { data: ['手机', '电脑', '平板'] },
series: [
{
name: '手机',
type: 'bar',
stack: 'total',
data: [320, 332, 301, 334],
itemStyle: { color: '#5470c6', borderRadius: [4, 4, 0, 0] }
},
{
name: '电脑',
type: 'bar',
stack: 'total',
data: [120, 132, 101, 134],
itemStyle: { color: '#91cc75' }
},
{
name: '平板',
type: 'bar',
stack: 'total',
data: [220, 182, 191, 234],
itemStyle: { color: '#fac858', borderRadius: [0, 0, 4, 4] }
}
]
};
四、双Y轴图表的”经典翻车现场”
双Y轴图表在某些场景下确实有用,比如一张图同时展示”销售额”(数值较大)和”利润率”(百分比,数值较小)。但如果用不好,就会变成两个图表硬塞进一个画布里,读者完全搞不清楚哪条线对应哪个轴。
坑8:双Y轴两条线量级接近——完全没有必要
错误示例代码:
// ❌ 错误:销售额和利润额量级相近,用双Y轴只会让图更乱
option = {
xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
yAxis: [
{ type: 'value', name: '销售额' },
{ type: 'value', name: '利润额', position: 'right' } // 量级相近,没必要放右边
],
series: [
{
name: '销售额',
type: 'line',
data: [820, 932, 901, 934, 1290, 1330]
},
{
name: '利润额',
type: 'line',
yAxisIndex: 1,
data: [780, 890, 860, 900, 1250, 1280] // 和销售额量级差不多
}
]
};
正确做法:量级相近时合并到同一个Y轴
// ✅ 正确:量级相近,单Y轴更清晰
option = {
xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
yAxis: { type: 'value' },
legend: { data: ['销售额', '利润额'] },
series: [
{
name: '销售额',
type: 'line',
data: [820, 932, 901, 934, 1290, 1330],
itemStyle: { color: '#5470c6' },
lineStyle: { width: 3 }
},
{
name: '利润额',
type: 'line',
data: [780, 890, 860, 900, 1250, 1280],
itemStyle: { color: '#ee6666' },
lineStyle: { width: 3, type: 'dashed' } // 用虚线区分,不用双Y轴
}
]
};
坑9:双Y轴不标注清楚——读者分不清哪条线是哪个轴
错误示例代码:
// ❌ 错误:双Y轴但没有清楚标注,读者会迷惑
option = {
xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
yAxis: [
{ type: 'value', name: '销售额(万)' },
{ type: 'value', name: '利润率(%)', position: 'right' }
],
series: [
{
type: 'bar',
data: [820, 932, 901, 934, 1290, 1330],
yAxisIndex: 0
},
{
type: 'line',
data: [15, 18, 16, 20, 25, 23],
yAxisIndex: 1
// 没有明显标识这条线对应右边轴
}
]
};
正确做法:双Y轴必须标注清楚,颜色要对应
// ✅ 正确:双Y轴,颜色一一对应,标注清晰
option = {
xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
yAxis: [
{
type: 'value',
name: '销售额(万)',
nameTextStyle: { color: '#5470c6', fontSize: 13 },
axisLine: { lineStyle: { color: '#5470c6' } },
axisLabel: { color: '#5470c6' }
},
{
type: 'value',
name: '利润率(%)',
nameTextStyle: { color: '#ee6666', fontSize: 13 },
position: 'right',
axisLine: { lineStyle: { color: '#ee6666' } },
axisLabel: { color: '#ee6666', formatter: '{value}%' },
splitLine: { show: false } // 右边的分割线隐藏,避免视觉混乱
}
],
legend: { data: ['销售额', '利润率'] },
series: [
{
name: '销售额',
type: 'bar',
data: [820, 932, 901, 934, 1290, 1330],
yAxisIndex: 0,
itemStyle: { color: '#5470c6', borderRadius: [4, 4, 0, 0] }
},
{
name: '利润率',
type: 'line',
data: [15, 18, 16, 20, 25, 23],
yAxisIndex: 1,
itemStyle: { color: '#ee6666' },
lineStyle: { color: '#ee6666', width: 3 },
label: {
show: true,
formatter: '{c}%',
color: '#ee6666',
fontSize: 11
}
}
]
};
五、散点图、气泡图和热力图的”身份混淆”
这三种图经常被混用,但它们表达的信息完全不同。
坑10:该用散点图却用了折线图——看不出相关性
散点图的核心价值是展示两个变量之间的相关性。如果你有一组数据想看看”广告投入”和”销售额”之间有没有关系,散点图是最佳选择。用折线图连接这些点,会给人一种”数据是连续的”的错觉,而实际上每个点都是独立的观测值。
错误示例代码:
// ❌ 错误:展示广告投入与销售额的相关性,却用了折线图
option = {
xAxis: { type: 'value', name: '广告投入(万)' },
yAxis: { type: 'value', name: '销售额(万)' },
series: [{
type: 'line',
data: [
[10, 50], [20, 80], [30, 120], [40, 150],
[50, 200], [60, 230], [70, 280], [80, 310]
],
connectNulls: true
// 折线连接让读者误以为数据是时间序列或连续变量
}]
};
正确做法:用散点图展示相关性
// ✅ 正确:散点图展示两个变量的相关性
option = {
xAxis: {
type: 'value',
name: '广告投入(万)',
nameTextStyle: { fontSize: 13 }
},
yAxis: {
type: 'value',
name: '销售额(万)',
nameTextStyle: { fontSize: 13 }
},
series: [{
type: 'scatter',
data: [
[10, 50], [20, 80], [30, 120], [40, 150],
[50, 200], [60, 230], [70, 280], [80, 310],
[25, 95], [55, 210], [65, 260]
],
symbolSize: 12,
itemStyle: {
color: '#5470c6',
opacity: 0.8
},
// 添加趋势线帮助理解相关性
markLine: {
silent: true,
lineStyle: { color: '#ee6666', type: 'dashed' },
data: [
{ xAxis: 10, yAxis: 50 },
{ xAxis: 80, yAxis: 350 }
]
}
}]
};
坑11:该用气泡图却用了普通散点图——丢失了一个维度
气泡图本质上就是三维散点图:X轴、Y轴和一个变量映射为气泡大小。如果你有三个维度的数据,普通散点图只能展示两个,第三个维度的信息就丢失了。
错误示例代码:
// ❌ 错误:有三个维度数据,却用普通散点图,丢失了"用户量"维度
option = {
xAxis: { type: 'value', name: '用户留存率(%)' },
yAxis: { type: 'value', name: '转化率(%)' },
series: [{
type: 'scatter',
data: [
[45, 12], [60, 18], [30, 8], [75, 25], [50, 15]
// 用户量数据完全丢失了
],
symbolSize: 10 // 所有点一样大,第三个维度没了
}]
};
正确做法:用气泡图展示三维数据
// ✅ 正确:气泡图展示三个维度——留存率、转化率、用户量
option = {
xAxis: {
type: 'value',
name: '用户留存率(%)',
nameTextStyle: { fontSize: 13 }
},
yAxis: {
type: 'value',
name: '转化率(%)',
nameTextStyle: { fontSize: 13 }
},
tooltip: {
formatter: function(params) {
return `${params.name}<br/>留存率: ${params.value[0]}%<br/>转化率: ${params.value[1]}%<br/>用户量: ${params.value[2]}万`;
}
},
series: [{
type: 'scatter',
data: [
{ value: [45, 12, 80], name: '产品A', itemStyle: { color: '#5470c6' } },
{ value: [60, 18, 120], name: '产品B', itemStyle: { color: '#91cc75' } },
{ value: [30, 8, 45], name: '产品C', itemStyle: { color: '#fac858' } },
{ value: [75, 25, 200], name: '产品D', itemStyle: { color: '#ee6666' } },
{ value: [50, 15, 150], name: '产品E', itemStyle: { color: '#73c0de' } }
],
symbolSize: function(data) {
return Math.sqrt(data[2]) * 3; // 气泡大小映射用户量
},
label: {
show: true,
formatter: '{b}',
position: 'top',
fontSize: 11
}
}]
};
六、仪表盘和进度图的”滥用警告”
仪表盘和进度图在展示”当前进度占总目标的比例”时非常直观,但它们有一个致命的问题:它们只能表达一个数值,无法表达趋势、分布或对比。
坑12:用仪表盘展示超过100%的数据——意义不明
仪表盘的设计初衷是展示”已完成的百分比”,范围通常是0-100。如果你用仪表盘展示”今年利润是去年的130%“,这就超出了仪表盘的语义范围,读者会困惑:指针指到130%是什么意思?是目标达成了30%还是超额完成了30%?
正确做法:用简单的数字+进度条,或者直接用柱状图对比
// ✅ 正确:用进度条展示目标完成度,简单清晰
option = {
series: [
{
type: 'gauge',
radius: '60%',
center: ['25%', '50%'],
max: 100,
axisLine: {
lineStyle: {
width: 20,
color: [
[0.6, '#ee6666'],
[0.8, '#fac858'],
[1, '#91cc75']
]
}
},
pointer: { itemStyle: { color: 'auto' } },
detail: {
formatter: '{value}%',
fontSize: 18,
color: '#333'
},
data: [{ value: 78, name: '年度目标完成度' }]
},
{
// 用柱状图展示另一个维度的对比,避免仪表盘滥用
type: 'bar',
xAxis: { type: 'category', data: ['今年', '去年'], splitLine: { show: false } },
yAxis: { type: 'value' },
data: [1500, 1200],
itemStyle: { color: '#5470c6', borderRadius: [4, 4, 0, 0] },
label: { show: true, position: 'top', formatter: '{c}万' },
grid: { left: '55%', right: '10%', top: '30%', bottom: '30%' }
}
]
};
七、颜色选择的”潜规则”
图表类型选对了,颜色用错了也会误导读者。颜色的选择有几个容易被忽视的规则。
坑13:用渐变色做柱状图——高度感知被扭曲
渐变色柱状图在视觉上很有吸引力,但它会给读者一个错误的暗示:柱子顶部的颜色更深,会让读者潜意识里认为顶部更重要或数值更大。如果你用从上到下的渐变色,读者对数值的判断会产生偏差。
错误示例代码:
// ❌ 错误:渐变色柱状图可能扭曲数值感知
option = {
xAxis: { type: 'category', data: ['A', 'B', 'C', 'D', 'E'] },
yAxis: { type: 'value' },
series: [{
type: 'bar',
data: [230, 190, 270, 150, 210],
itemStyle: {
// 渐变会让柱子的不同部分视觉上"权重"不同
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
])
}
}]
};
正确做法:用纯色或微渐变,保持数值感知的准确性
// ✅ 正确:纯色柱状图,数值感知准确
option = {
xAxis: { type: 'category', data: ['A', 'B', 'C', 'D', 'E'] },
yAxis: { type: 'value' },
series: [{
type: 'bar',
data: [230, 190, 270, 150, 210],
itemStyle: {
color: '#5470c6',
borderRadius: [4, 4, 0, 0]
},
label: {
show: true,
position: 'top',
formatter: '{c}'
}
}]
};
坑14:用暖色表示下降、冷色表示上升——违反直觉
在大多数文化中,暖色(红、橙)与”危险”“下降”关联,冷色(蓝、绿)与”安全”“增长”关联。如果你在图表中用红色表示增长、蓝色表示下降,读者会本能地感到不安,即使数据本身是好的。
八、几个实用的避坑检查清单
写到这里,我整理了一份实用的检查清单,每次做完图表之后过一遍,基本能避开90%的常见错误:
图表类型选择检查:
- [ ] 我的数据是时间序列吗?是 → 优先考虑折线图
- [ ] 我的数据是类别对比吗?是 → 优先考虑柱状图
- [ ] 我的数据是占比关系吗?是 → 优先考虑饼图,但切片不超过7个
- [ ] 我的数据有正有负吗?是 → 绝对不能用饼图
- [ ] 我想展示相关性吗?是 → 用散点图,不要用折线图连接
双Y轴检查:
- [ ] 两个变量的量级差异是否超过10倍?不超过 → 合并到单Y轴
- [ ] 是否清晰标注了左右Y轴分别对应哪个系列?
- [ ] 系列的颜色和轴的顏色是否一一对应?
饼图检查:
- [ ] 切片数量是否在5-7个以内?
- [ ] 是否有负数或零值数据?有 → 换用柱状图
- [ ] 是否避免了3D效果?
颜色检查:
- [ ] 渐变色是否影响了数值的准确感知?
- [ ] 颜色是否符合数据的语义(增长用暖色/绿色,下降用冷色/红色)?
- [ ] 色盲友好的配色方案是否被考虑?
九、实战:一个完整的数据看板案例
下面这个例子把前面提到的各种原则综合起来,展示一个完整的销售数据看板。你可以直接复制到ECharts官方编辑器里跑起来看效果。
// 完整销售数据看板 - 综合应用各项原则
const chartDom = document.getElementById('main');
const myChart = echarts.init(chartDom);
// 模拟数据
const months = ['1月', '2月', '3月', '4月', '5月', '6月'];
const salesData = [820, 932, 901, 934, 1290, 1330];
const profitData = [156, 186, 162, 190, 268, 278];
const profitRate = salesData.map((s, i) => ((profitData[i] / s) * 100).toFixed(1));
const option = {
backgroundColor: '#f5f7fa',
title: {
text: '2024年上半年销售数据看板',
left: 'center',
textStyle: { fontSize: 18, fontWeight: 'bold', color: '#333' }
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'cross' },
backgroundColor: 'rgba(255,255,255,0.95)',
borderColor: '#ddd',
textStyle: { color: '#333' }
},
legend: {
data: ['销售额', '利润额', '利润率'],
top: 35,
textStyle: { color: '#666' }
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: 90,
containLabel: true
},
xAxis: {
type: 'category',
data: months,
axisLine: { lineStyle: { color: '#ccc' } },
axisLabel: { color: '#666' }
},
yAxis: [
{
type: 'value',
name: '金额(万元)',
position: 'left',
nameTextStyle: { color: '#5470c6' },
axisLine: { lineStyle: { color: '#5470c6' } },
axisLabel: { color: '#5470c6' },
splitLine: { lineStyle: { color: '#eee', type: 'dashed' } }
},
{
type: 'value',
name: '利润率(%)',
position: 'right',
nameTextStyle: { color: '#ee6666' },
axisLine: { lineStyle: { color: '#ee6666' } },
axisLabel: { color: '#ee6666', formatter: '{value}%' },
splitLine: { show: false }
}
],
series: [
{
name: '销售额',
type: 'bar',
data: salesData,
yAxisIndex: 0,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#5470c6' },
{ offset: 1, color: '#3a5aad' }
]),
borderRadius: [4, 4, 0, 0]
},
barWidth: '40%',
label: {
show: true,
position: 'top',
formatter: '{c}',
color: '#5470c6',
fontSize: 11
}
},
{
name: '利润额',
type: 'bar',
data: profitData,
yAxisIndex: 0,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#91cc75' },
{ offset: 1, color: '#6aab4e' }
]),
borderRadius: [4, 4, 0, 0]
},
barWidth: '40%',
label: {
show: true,
position: 'top',
formatter: '{c}',
color: '#91cc75',
fontSize: 11
}
},
{
name: '利润率',
type: 'line',
data: profitRate,
yAxisIndex: 1,
smooth: true,
symbol: 'circle',
symbolSize: 8,
lineStyle: { color: '#ee6666', width: 3 },
itemStyle: { color: '#ee6666' },
label: {
show: true,
position: 'top',
formatter: '{c}%',
color: '#ee6666',
fontSize: 11,
fontWeight: 'bold'
},
// 添加标记线突出关键节点
markPoint: {
data: [
{ type: 'max', name: '最高利润率' },
{ type: 'min', name: '最低利润率' }
],
itemStyle: { color: '#ee6666' }
}
}
]
};
myChart.setOption(option);
// 响应式
window.addEventListener('resize', () => myChart.resize());
这个看板同时使用了柱状图(展示销售额和利润额的对比)和折线图(展示利润率趋势),双Y轴的量级差异足够大(金额是百级别,利润率是个位数),颜色一一对应,标签清晰,应该是一个比较规范的实战案例了。
写在最后
图表选择的本质,不是”哪种好看用哪种”,而是”哪种最能准确传达数据背后的信息”。每次打开ECharts配置面板之前,先问自己三个问题:我想让读者看到什么?这个数据是什么性质?哪种图表能最少干扰地传达这个信息?
把这三个问题想清楚,图表类型基本就不会选错了。剩下的只是细节上的打磨——颜色、标注、交互——这些锦上添花的东西。
希望这篇文章能帮你避开那些我踩过的坑。如果你在实际项目中遇到了拿不准的图表选择,欢迎带着你的数据来聊聊,我们一起分析分析。
