跳转到内容

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('缩放'));

十一、性能优化

策略

说明

实现

数据降采样

大数据量时减少渲染点

sampling: 'lttb'

增量渲染

逐步加载渲染

progressive: 200

SVG 渲染

移动端/低性能场景

echarts.init(dom, null, { renderer: 'svg' })

关闭动画

大数据量更新时

animation: false

销毁图表

释放内存

chart.dispose()

// 大数据量配置
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