ECharts 数据可视化
一、ECharts 概述
1.1 什么是 ECharts
ECharts(Enterprise Charts)是百度开源的一个基于 JavaScript 的数据可视化库,底层依赖轻量级 Canvas 库 ZRender。它提供了直观、交互丰富、可高度个性化定制的数据可视化图表。
1.2 核心特性
|
特性 |
说明 |
|---|---|
|
丰富的图表类型 |
折线图、柱状图、饼图、散点图、地图、热力图、关系图、树图等 40+ 种 |
|
强大的交互组件 |
图例切换、数据区域缩放、Tooltip 提示、Brush 选取等 |
|
多端适配 |
支持 PC、移动端、大屏,提供 SVG 渲染器用于移动端优化 |
|
大数据量渲染 |
支持增量渲染、WebGL 加速(echarts-gl),万级数据流畅展示 |
|
动态数据 |
支持动态数据更新与动画过渡 |
|
主题定制 |
内置多套主题,支持自定义主题 |
二、安装与快速上手
2.1 安装
# npm 安装
npm install echarts
# CDN 引入
# <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
2.2 快速上手
import * as echarts from 'echarts';
// 1. 初始化
const chartDom = document.getElementById('main');
const myChart = echarts.init(chartDom);
// 2. 配置
const option = {
title: { text: '月度销售数据' },
tooltip: {},
legend: { data: ['销售额'] },
xAxis: { data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
yAxis: {},
series: [{
name: '销售额',
type: 'bar',
data: [120, 200, 150, 80, 70, 110]
}]
};
// 3. 渲染
myChart.setOption(option);
// 4. 自适应
window.addEventListener('resize', () => {
myChart.resize();
});
<div id="main" style="width: 800px; height: 500px;"></div>
三、核心配置结构
const option = {
title: { text: '', subtext: '', left: 'center' }, // 标题
tooltip: { trigger: 'axis' }, // 提示框
legend: { data: [], orient: 'horizontal' }, // 图例
toolbox: { feature: { saveAsImage: {}, dataView: {} } }, // 工具栏
xAxis: { type: 'category', data: [] }, // X 轴
yAxis: { type: 'value' }, // Y 轴
series: [{ type: 'bar', data: [] }], // 系列数据(核心)
color: ['#5470c6', '#91cc75', '#fac858', '#ee6666'],// 调色盘
grid: { left: '10%', right: '10%', bottom: '15%' }, // 网格
dataZoom: [{ type: 'slider' }, { type: 'inside' }] // 数据缩放
};
四、图表类型详解
4.1 折线图(Line)
series: [{
name: 'PV',
type: 'line',
data: [820, 932, 901, 934, 1290, 1330, 1320],
smooth: true, // 平滑曲线
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)' }
])
},
markPoint: { // 标注最大值/最小值
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' }
]
},
markLine: { // 标记平均线
data: [{ type: 'average', name: '平均值' }]
}
}]
折线图常用配置:
|
属性 |
说明 |
可选值 |
|---|---|---|
|
smooth |
平滑曲线 |
true/false |
|
step |
阶梯线 |
'start'/'middle'/'end' |
|
areaStyle |
区域填充 |
{ color: 'rgba(...)' } |
|
symbol |
数据点图形 |
'circle'/'rect'/'triangle'/'none' |
|
stack |
堆叠标识 |
'groupName' |
4.2 柱状图(Bar)
series: [
{
name: '产品A', type: 'bar',
data: [320, 332, 301, 334],
itemStyle: { borderRadius: [5, 5, 0, 0] }, // 圆角
label: { show: true, position: 'top' } // 数据标签
},
{
name: '产品B', type: 'bar',
data: [220, 182, 191, 234],
stack: 'total' // 堆叠
}
]
柱状图变体:
- 堆叠柱状图:
stack: 'total' - 横向柱状图:互换 xAxis 和 yAxis
- 圆角:
itemStyle: { borderRadius: [5, 5, 0, 0] }
4.3 饼图(Pie)
series: [{
name: '来源',
type: 'pie',
radius: ['40%', '70%'], // 环形图 [内半径, 外半径]
center: ['50%', '50%'], // 圆心位置
emphasis: { // 高亮样式
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
label: { formatter: '{b}: {d}%' },
data: [
{ value: 1048, name: '搜索引擎' },
{ value: 735, name: '直接访问' },
{ value: 580, name: '社交媒体' },
{ value: 484, name: '邮件营销' },
{ value: 300, name: '联盟广告' }
]
}]
饼图常用配置:
|
属性 |
说明 |
示例 |
|---|---|---|
|
radius |
半径(数组为环形图) |
'60%' 或 ['40%', '70%'] |
|
roseType |
玫瑰图 |
'radius' / 'area' |
|
selectedMode |
选中模式 |
'single' / 'multiple' |
4.4 散点图(Scatter)
series: [{
type: 'scatter',
symbolSize: val => Math.sqrt(val[2]) * 4, // 气泡大小
data: [
[23, 580, 1], [25, 1200, 2], [32, 2500, 4],
[35, 1800, 3], [40, 3200, 5], [45, 2100, 3.5]
],
itemStyle: { opacity: 0.7 }
}]
4.5 雷达图(Radar)
option = {
radar: {
indicator: [
{ name: '前端', max: 100 },
{ name: '后端', max: 100 },
{ name: '数据库', max: 100 },
{ name: '运维', max: 100 },
{ name: '算法', max: 100 }
],
shape: 'polygon'
},
series: [{
type: 'radar',
data: [
{
name: '张三',
value: [85, 60, 70, 55, 45],
areaStyle: { color: 'rgba(84,112,198,0.2)' }
}
]
}]
};
4.6 仪表盘(Gauge)
series: [{
type: 'gauge',
min: 0, max: 100,
progress: { show: true, width: 18 },
axisLine: {
lineStyle: {
width: 18,
color: [[0.3, '#67e0e3'], [0.7, '#37a2da'], [1, '#fd666d']]
}
},
detail: { valueAnimation: true, formatter: '{value}%', fontSize: 30 },
data: [{ value: 78.5, name: '完成率' }]
}]
4.7 漏斗图(Funnel)
series: [{
type: 'funnel',
sort: 'descending',
gap: 2,
label: { show: true, position: 'inside' },
data: [
{ value: 1000, name: '访问' },
{ value: 800, name: '浏览' },
{ value: 600, name: '加购' },
{ value: 400, name: '下单' },
{ value: 200, name: '支付' }
]
}]
4.8 热力图(Heatmap)
option = {
xAxis: { type: 'category', data: ['周一', '周二', '周三', '周四', '周五'] },
yAxis: { type: 'category', data: ['9:00', '10:00', '11:00', '12:00', '14:00', '15:00'] },
visualMap: {
min: 0, max: 100,
calculable: true,
inRange: { color: ['#e0ffff', '#006edd'] }
},
series: [{
type: 'heatmap',
data: [[0, 0, 5], [0, 1, 15], [0, 2, 45], [1, 0, 20], [1, 1, 35]],
label: { show: true }
}]
};
4.9 地图(Map)
// 需先注册地图数据:echarts.registerMap('china', geoJson);
option = {
visualMap: {
min: 0, max: 1000,
text: ['高', '低'],
inRange: { color: ['#e0f3f8', '#045a8d'] }
},
series: [{
type: 'map',
map: 'china',
roam: true, // 缩放漫游
label: { show: true },
emphasis: {
label: { show: true, fontSize: 14 },
itemStyle: { areaColor: '#f3b029' }
},
data: [
{ name: '北京', value: 932 },
{ name: '上海', value: 876 },
{ name: '广东', value: 798 }
]
}]
};
4.10 其他图表类型速查
|
图表类型 |
type 值 |
适用场景 |
|---|---|---|
|
蜡烛图(K 线) |
'candlestick' |
股票/金融价格走势 |
|
盒须图 |
'boxplot' |
数据分布、离群值分析 |
|
桑基图 |
'sankey' |
流量/能量转移分析 |
|
关系图 |
'graph' |
社交网络、知识图谱 |
|
树图 |
'tree' / 'treemap' |
层级数据、文件系统 |
|
旭日图 |
'sunburst' |
层次占比 |
|
象形柱图 |
'pictorialBar' |
生动的柱形对比 |
|
河流图 |
'themeRiver' |
事件随时间变化 |
五、交互组件详解
5.1 Tooltip(提示框)
tooltip: {
trigger: 'axis', // 'item' | 'axis' | 'none'
backgroundColor: 'rgba(50,50,50,0.9)',
borderColor: '#333',
textStyle: { color: '#fff', fontSize: 12 },
formatter: function(params) {
return `${params[0].name}<br/>${params[0].marker} ${params[0].seriesName}: ${params[0].value}`;
}
}
5.2 Legend(图例)
legend: {
type: 'scroll', // 'plain' | 'scroll'(图例过多时可滚动)
orient: 'horizontal',
left: 'center',
top: 'bottom',
selected: { '产品A': true, '产品B': false }, // 初始选中状态
icon: 'roundRect'
}
5.3 Toolbox(工具栏)
toolbox: {
feature: {
saveAsImage: { title: '保存', pixelRatio: 2 }, // 导出高清图
dataView: { readOnly: false },
magicType: { type: ['line', 'bar', 'stack'] }, // 切换图表类型
restore: { title: '还原' }
}
}
5.4 DataZoom(数据区域缩放)
dataZoom: [
{ type: 'slider', start: 0, end: 100 }, // 滑块型
{ type: 'inside', zoomOnMouseWheel: true } // 内置型(鼠标滚轮)
]
5.5 VisualMap(视觉映射)
visualMap: {
type: 'continuous', // 连续型
// type: 'piecewise', // 分段型
min: 0, max: 1000,
inRange: { color: ['#50a3ba', '#eac736', '#d94e5d'] }
}
六、样式与主题
6.1 内置主题
|
主题名 |
风格 |
|---|---|
|
default |
默认亮色 |
|
dark |
暗色主题 |
|
vintage |
复古风格 |
|
macarons |
马卡龙色系 |
|
infographic |
信息图风格 |
|
shine |
亮光风格 |
|
roma |
罗马风格 |
import 'echarts/theme/macarons.js';
const chart = echarts.init(dom, 'macarons');
6.2 动画配置
option = {
animation: true,
animationDuration: 1000, // 初始动画时长(ms)
animationDurationUpdate: 300, // 数据更新动画时长
animationEasing: 'cubicInOut',
series: [{
type: 'bar',
animationDelay: (idx) => idx * 50 // 逐条延迟(序列动画)
}]
};
6.3 常用样式速查
// 渐变颜色
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 1, color: '#188df0' }
])
}
// 全局文字样式
textStyle: { fontFamily: 'Microsoft YaHei', fontSize: 12 }
// 坐标轴样式
xAxis: {
axisLine: { lineStyle: { color: '#ddd' } },
axisTick: { show: false },
axisLabel: { color: '#666' }
}
// 分割线样式
yAxis: {
splitLine: { lineStyle: { type: 'dashed', color: '#eee' } }
}
七、动态数据与异步加载
7.1 异步加载
const chart = echarts.init(document.getElementById('main'));
chart.showLoading({ text: '数据加载中...', color: '#5470c6' });
fetch('/api/chart-data')
.then(res => res.json())
.then(data => {
chart.hideLoading();
chart.setOption({
xAxis: { data: data.categories },
series: [{ type: 'bar', data: data.values }]
});
});
7.2 动态更新数据
// 增量追加(适合大数据量持续追加)
chart.appendData({ seriesIndex: 0, data: [newValue] });
7.3 dataset(统一数据源)
option = {
dataset: {
source: [
['product', '2015', '2016', '2017'],
['Matcha Latte', 43.3, 85.8, 93.7],
['Milk Tea', 83.1, 73.4, 55.1],
['Cheese Cocoa', 86.4, 65.2, 82.5]
]
},
xAxis: { type: 'category' },
yAxis: {},
series: [{ type: 'bar' }, { type: 'bar' }]
};
八、框架集成
8.1 Vue 3 集成
<template>
<div ref="chartRef" style="width: 100%; height: 400px;"></div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import * as echarts from 'echarts';
const chartRef = ref(null);
let chart = null;
const props = defineProps({ options: { type: Object, required: true } });
onMounted(() => {
chart = echarts.init(chartRef.value);
chart.setOption(props.options);
window.addEventListener('resize', () => chart?.resize());
});
onBeforeUnmount(() => {
chart?.dispose();
});
watch(() => props.options, (newOpt) => {
chart?.setOption(newOpt, true);
}, { deep: true });
</script>
8.2 React 集成
import { useEffect, useRef } from 'react';
import * as echarts from 'echarts';
function EChart({ option, style = { width: '100%', height: 400 } }) {
const ref = useRef<HTMLDivElement>(null);
const chartRef = useRef<echarts.ECharts | null>(null);
useEffect(() => {
if (!ref.current) return;
chartRef.current = echarts.init(ref.current);
const onResize = () => chartRef.current?.resize();
window.addEventListener('resize', onResize);
return () => {
window.removeEventListener('resize', onResize);
chartRef.current?.dispose();
};
}, []);
useEffect(() => {
chartRef.current?.setOption(option, true);
}, [option]);
return <div ref={ref} style={style} />;
}
8.3 社区封装库
|
库 |
框架 |
特点 |
|---|---|---|
|
vue-echarts |
Vue 2/3 |
官方推荐的 Vue 封装 |
|
echarts-for-react |
React |
轻量 React 封装 |
|
ngx-echarts |
Angular |
Angular 封装 |
九、数据大屏方案
9.1 大屏适配
// rem/scale 自适应
function setRem() {
const baseWidth = 1920, baseHeight = 1080;
const scale = Math.min(
window.innerWidth / baseWidth,
window.innerHeight / baseHeight
);
document.documentElement.style.fontSize = `${16 * scale}px`;
}
window.addEventListener('resize', setRem);
9.2 大屏优化建议
- 使用 SVG 渲染器:
echarts.init(dom, null, { renderer: 'svg' }) - 减少不必要的动画
- 数据降采样(大数据量时提前聚合)
- 懒加载非首屏图表
- 使用 WebSocket 推送数据替代轮询
十、常见场景实战
10.1 多图表联动
const chart1 = echarts.init(document.getElementById('chart1'));
const chart2 = echarts.init(document.getElementById('chart2'));
// 连接两个图表,dataZoom/tooltip/legend 操作同步
echarts.connect('group1');
chart1.group = 'group1';
chart2.group = 'group1';
10.2 导出高清图片
const url = chart.getDataURL({ type: 'png', pixelRatio: 2, backgroundColor: '#fff' });
const link = document.createElement('a');
link.href = url;
link.download = 'chart.png';
link.click();
10.3 点击事件与下钻
chart.on('click', (params) => {
if (params.componentType === 'series') {
const category = params.name;
// 下钻到明细
chart.setOption({ /* 新配置 */ });
}
});
// 常用事件
chart.on('mouseover', params => console.log('hover'));
chart.on('legendselectchanged', params => console.log('图例切换'));
chart.on('datazoom', params => console.log('缩放'));
十一、性能优化
|
策略 |
说明 |
实现 |
|---|---|---|
|
数据降采样 |
大数据量时减少渲染点 |
|
|
增量渲染 |
逐步加载渲染 |
|
|
SVG 渲染 |
移动端/低性能场景 |
|
|
关闭动画 |
大数据量更新时 |
|
|
销毁图表 |
释放内存 |
|
// 大数据量配置
series: [{
type: 'line',
sampling: 'lttb', // 降采样算法
large: true, // 大数据量优化模式
largeThreshold: 2000, // 超过此数量启用优化
progressive: 400 // 分片渲染
}]
十二、常见问题
图表不显示
- 确认容器有明确宽高
- 确认
setOption在 DOM 渲染之后(Vue onMounted / React useEffect)
图表大小不适配
- 监听
window.resize调用chart.resize() - 或使用 ResizeObserver 监听容器变化
数据更新不生效
- 使用
chart.setOption(option, true)第二个参数 true 完全替换
十三、常用资源
- 官方文档:https://echarts.apache.org/zh/index.html
- 示例库:https://echarts.apache.org/examples/zh/index.html
- 主题构建:https://echarts.apache.org/zh/theme-builder.html
- GitHub:https://github.com/apache/echarts
- Make A Pie(社区作品):https://www.makeapie.cn/echarts