柱状图 (堆叠/横向)
ECharts 柱状图完全指南:对比分析的利器
📖 概述
柱状图(Bar Chart)是最经典的数据可视化图表之一,通过柱子的高度或长度来展示数据的大小,适用于分类数据的对比分析。ECharts 的柱状图支持丰富的变体和配置选项。
核心特点:
- 支持垂直和横向布局
- 支持堆叠和分组
- 支持负值显示
- 支持圆角柱形
- 大数据量优化
🔍 核心概念
1. 基础柱状图
javascript
option = {
xAxis: {
type: 'category',
data: ['产品A', '产品B', '产品C', '产品D']
},
yAxis: {
type: 'value'
},
series: [{
type: 'bar',
data: [120, 200, 150, 80]
}]
};1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
2. 横向柱状图
javascript
option = {
xAxis: { type: 'value' },
yAxis: {
type: 'category',
data: ['产品A', '产品B', '产品C', '产品D']
},
series: [{
type: 'bar',
data: [120, 200, 150, 80]
}]
};1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
适用场景: 类别名称较长时,横向布局更易阅读
3. 堆叠柱状图
javascript
option = {
xAxis: { type: 'category', data: categories },
yAxis: { type: 'value' },
series: [
{
name: '线上销售',
type: 'bar',
stack: 'total', // 堆叠标识
data: [120, 132, 101, 134]
},
{
name: '线下销售',
type: 'bar',
stack: 'total', // 相同的 stack 值会堆叠
data: [220, 182, 191, 234]
}
]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
4. 分组柱状图
javascript
// 不设置 stack,默认就是分组
option = {
xAxis: { type: 'category', data: categories },
yAxis: { type: 'value' },
series: [
{
name: '2023年',
type: 'bar',
data: [120, 132, 101, 134]
},
{
name: '2024年',
type: 'bar',
data: [220, 182, 191, 234]
}
]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
💡 使用场景
场景 1: 销售排行榜
javascript
option = {
title: { text: '产品销售排行榜 TOP10' },
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: { type: 'value' },
yAxis: {
type: 'category',
data: ['产品J', '产品I', '产品H', '产品G', '产品F',
'产品E', '产品D', '产品C', '产品B', '产品A'],
inverse: true // 反转顺序,第一名在最上面
},
series: [{
type: 'bar',
data: [820, 832, 901, 934, 1290, 1330, 1320, 1500, 1800, 2100],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
])
},
label: {
show: true,
position: 'right',
formatter: '{c}件'
}
}]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
场景 2: 预算 vs 实际对比
javascript
option = {
legend: { data: ['预算', '实际', '完成率'] },
xAxis: { type: 'category', data: departments },
yAxis: [
{ type: 'value', name: '金额(万元)' },
{ type: 'value', name: '完成率', max: 100, axisLabel: { formatter: '{value}%' } }
],
series: [
{
name: '预算',
type: 'bar',
data: [100, 120, 150, 80, 200],
itemStyle: { color: '#91cc75' }
},
{
name: '实际',
type: 'bar',
data: [90, 130, 140, 85, 180],
itemStyle: { color: '#5470c6' }
},
{
name: '完成率',
type: 'line',
yAxisIndex: 1,
data: [90, 108, 93, 106, 90],
lineStyle: { color: '#ee6666', width: 2 }
}
]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
场景 3: 正负值对比
javascript
// 利润盈亏分析
option = {
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
xAxis: { type: 'category', data: months },
yAxis: { type: 'value' },
series: [{
name: '净利润',
type: 'bar',
data: [120, -50, 80, -30, 150, 200, -80, 100, 120, -40, 90, 160],
itemStyle: {
color: function(params) {
return params.value >= 0 ? '#91cc75' : '#ee6666'; // 盈利绿色,亏损红色
}
},
label: {
show: true,
formatter: '{c}万'
}
}]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
🔧 配置详解
柱形样式
javascript
series: [{
type: 'bar',
// 柱形宽度
barWidth: '60%', // 固定百分比
barMaxWidth: 100, // 最大宽度(像素)
barMinWidth: 10, // 最小宽度(像素)
barGap: '30%', // 不同系列间距
barCategoryGap: '20%', // 类目间距
// 圆角
itemStyle: {
borderRadius: [5, 5, 0, 0], // [左上, 右上, 右下, 左下]
color: '#5470c6',
opacity: 0.8
},
// 边框
emphasis: {
itemStyle: {
borderColor: '#333',
borderWidth: 2
}
}
}]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
背景色和标记
javascript
series: [{
type: 'bar',
data: data,
// 显示背景色
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)',
borderRadius: 3
},
// 标记线
markLine: {
data: [
{ type: 'average', name: '平均值' },
{ yAxis: 150, name: '目标值' }
]
},
// 标记点
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' }
]
}
}]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
标签配置
javascript
series: [{
type: 'bar',
label: {
show: true,
position: 'top', // 'top' | 'bottom' | 'inside' | 'left' | 'right'
formatter: '{c}',
fontSize: 12,
color: '#333',
rotate: 45 // 旋转角度(适合长文本)
}
}]1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
📝 代码示例
示例 1: 完整的堆叠柱状图
javascript
const option = {
title: {
text: '季度销售构成分析',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }
},
legend: {
data: ['电子产品', '服装', '食品', '其他'],
top: 30
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['Q1', 'Q2', 'Q3', 'Q4']
},
yAxis: {
type: 'value',
axisLabel: { formatter: '¥{value}' }
},
series: [
{
name: '电子产品',
type: 'bar',
stack: 'total',
emphasis: { focus: 'series' },
data: [320, 302, 301, 334],
itemStyle: { color: '#5470c6' }
},
{
name: '服装',
type: 'bar',
stack: 'total',
emphasis: { focus: 'series' },
data: [120, 132, 101, 134],
itemStyle: { color: '#91cc75' }
},
{
name: '食品',
type: 'bar',
stack: 'total',
emphasis: { focus: 'series' },
data: [220, 182, 191, 234],
itemStyle: { color: '#fac858' }
},
{
name: '其他',
type: 'bar',
stack: 'total',
emphasis: { focus: 'series' },
data: [150, 212, 201, 154],
itemStyle: { color: '#ee6666' }
}
]
};
chart.setOption(option);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
示例 2: 瀑布图效果
javascript
// 使用透明柱实现瀑布图
const rawData = [900, 345, 393, -108, -154, 135, 178, 286, -119, -361, -203];
const helpData = [];
let base = 0;
rawData.forEach((item, index) => {
if (item < 0) {
helpData.push(base + item);
} else {
helpData.push(base);
}
base += item;
});
option = {
title: { text: '收支瀑布图' },
tooltip: {
trigger: 'axis',
formatter: function(params) {
const data = params[0];
return `${data.name}<br/>${data.seriesName}: ${data.value}`;
}
},
xAxis: {
type: 'category',
data: ['初始', '项1', '项2', '项3', '项4', '项5', '项6', '项7', '项8', '项9', '项10', '最终']
},
yAxis: { type: 'value' },
series: [
{
name: '辅助',
type: 'bar',
stack: '总量',
itemStyle: {
borderColor: 'transparent',
color: 'transparent'
},
emphasis: {
itemStyle: {
borderColor: 'transparent',
color: 'transparent'
}
},
data: helpData
},
{
name: '数值',
type: 'bar',
stack: '总量',
data: rawData.map(v => Math.abs(v)),
itemStyle: {
color: function(params) {
return rawData[params.dataIndex] >= 0 ? '#91cc75' : '#ee6666';
}
}
}
]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
示例 3: 动态排序柱状图
javascript
class RaceChart {
constructor(dom) {
this.chart = echarts.init(dom);
this.data = [];
this.init();
}
init() {
this.chart.setOption({
title: { text: '实时排行榜' },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: { type: 'value' },
yAxis: {
type: 'category',
inverse: true,
animationDuration: 300,
animationDurationUpdate: 300
},
series: [{
type: 'bar',
data: [],
realtimeSort: true, // 开启实时排序
label: {
show: true,
position: 'right',
valueAnimation: true
},
itemStyle: {
color: function(params) {
const colors = ['#5470c6', '#91cc75', '#fac858', '#ee6666'];
return colors[params.dataIndex % colors.length];
}
}
}],
animationDuration: 3000,
animationDurationUpdate: 3000,
animationEasing: 'linear',
animationEasingUpdate: 'linear'
});
// 模拟数据更新
setInterval(() => this.updateData(), 2000);
}
updateData() {
// 生成随机数据并排序
const newData = Array.from({ length: 10 }, (_, i) => ({
name: `产品${String.fromCharCode(65 + i)}`,
value: Math.floor(Math.random() * 1000)
})).sort((a, b) => a.value - b.value);
this.chart.setOption({
series: [{
data: newData
}]
});
}
}
// 使用
const raceChart = new RaceChart(document.getElementById('chart'));1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
⚠️ 常见问题
Q1: 柱子太细或太粗怎么办?
javascript
// 调整柱子宽度
series: [{
barWidth: '50%', // 相对宽度
barMaxWidth: 80, // 最大宽度限制
barCategoryGap: '30%' // 类目间距
}]1
2
3
4
5
6
2
3
4
5
6
Q2: 如何显示柱子的具体数值?
javascript
series: [{
label: {
show: true,
position: 'top',
formatter: '{c}' // {c} 表示数值
}
}]1
2
3
4
5
6
7
2
3
4
5
6
7
Q3: 堆叠柱状图如何显示总计?
javascript
// 方法: 添加一个额外的系列显示总计
series: [
{ name: 'A', type: 'bar', stack: 'total', data: [...] },
{ name: 'B', type: 'bar', stack: 'total', data: [...] },
{
name: '总计',
type: 'line',
data: totalData,
label: { show: true, position: 'top' }
}
]1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Q4: 如何实现渐变色彩?
javascript
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 1, color: '#188df0' }
])
}1
2
3
4
5
6
2
3
4
5
6
🎯 最佳实践
1. 选择合适的方向
- 纵向柱状图: 类别较少(≤ 10),名称较短
- 横向柱状图: 类别较多,名称较长
2. 颜色搭配原则
javascript
// 单一系列: 统一色调
color: '#5470c6'
// 多个系列: 区分明显的颜色
colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666']
// 堆叠系列: 同色系不同深浅
// 或使用完全不同的颜色区分1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
3. 数据标签策略
- 柱子足够高时才显示标签(
barHeight > 30px) - 使用
rotate旋转避免重叠 - 外部空间不足时使用
position: 'inside'
4. 性能优化
javascript
// 大数据量优化
series: [{
large: true, // 开启大数据模式
largeThreshold: 400, // 阈值
progressive: 1000, // 渐进式渲染
animation: false // 关闭动画
}]1
2
3
4
5
6
7
2
3
4
5
6
7
📊 性能指标
| 数据量 | 推荐配置 | 渲染时间 | 内存占用 |
|---|---|---|---|
| < 50 | 默认配置 | < 10ms | ~2MB |
| 50-500 | 关闭部分特效 | 10-50ms | ~5MB |
| 500-5000 | 开启 large 模式 | 50-200ms | ~15MB |
| > 5000 | 关闭动画+渐进式 | 200-500ms | ~30MB |
🔗 相关链接
- 折线图.md)
- 饼图.md)
- 混合图表.md)
- 性能优化
- 官方示例
最后更新: 2026-04-22
难度等级: ⭐⭐
预计阅读时间: 18 分钟
基础柱状图
{
"title": {
"text": "季度销售对比",
"left": "center"
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"legend": {
"data": [
"产品A",
"产品B"
],
"top": "10%"
},
"xAxis": {
"type": "category",
"data": [
"Q1",
"Q2",
"Q3",
"Q4"
]
},
"yAxis": {
"type": "value"
},
"series": [
{
"name": "产品A",
"type": "bar",
"data": [
120,
200,
150,
80
],
"itemStyle": {
"color": "#5470c6"
}
},
{
"name": "产品B",
"type": "bar",
"data": [
100,
150,
180,
120
],
"itemStyle": {
"color": "#91cc75"
}
}
]
}堆叠柱状图效果
{
"title": {
"text": "堆叠柱状图",
"left": "center"
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"legend": {
"data": [
"直接访问",
"邮件营销",
"联盟广告"
],
"top": "10%"
},
"xAxis": {
"type": "category",
"data": [
"周一",
"周二",
"周三",
"周四",
"周五",
"周六",
"周日"
]
},
"yAxis": {
"type": "value"
},
"series": [
{
"name": "直接访问",
"type": "bar",
"stack": "total",
"data": [
320,
332,
301,
334,
390,
330,
320
],
"emphasis": {
"focus": "series"
}
},
{
"name": "邮件营销",
"type": "bar",
"stack": "total",
"data": [
120,
132,
101,
134,
90,
230,
210
],
"emphasis": {
"focus": "series"
}
},
{
"name": "联盟广告",
"type": "bar",
"stack": "total",
"data": [
220,
182,
191,
234,
290,
330,
310
],
"emphasis": {
"focus": "series"
}
}
]
}横向柱状图效果
{
"title": {
"text": "各国GDP对比",
"left": "center"
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"grid": {
"left": "3%",
"right": "4%",
"bottom": "3%",
"containLabel": true
},
"xAxis": {
"type": "value"
},
"yAxis": {
"type": "category",
"data": [
"中国",
"日本",
"德国",
"英国",
"法国",
"美国"
],
"inverse": false
},
"series": [
{
"name": "GDP(万亿美元)",
"type": "bar",
"data": [
17.7,
4.9,
4.2,
3.1,
2.9,
25.5
],
"itemStyle": {
"color": {
"type": "linear",
"x": 0,
"y": 0,
"x2": 1,
"y2": 0,
"colorStops": [
{
"offset": 0,
"color": "#5470c6"
},
{
"offset": 1,
"color": "#91cc75"
}
]
}
},
"label": {
"show": true,
"position": "right",
"formatter": "{c}"
}
}
]
}