零基础学Echarts从零搭建数据可视化图表实战入门教程视频
先把话说明白
我之前也是啥都不懂,第一次听到Echarts的时候,以为跟什么神秘的魔法符号一样高深莫测。后来折腾了几天才发现,这家伙其实是个特别”讲道理”的朋友——只要你给它一个图表,给它一组数据,再给它几个简单的配置项,它就屁颠屁颠把漂亮的图表给你画出来。
今天这篇教程,我就是想跟你唠唠我是怎么从一只小菜鸟变成能熟练玩Echarts的人的。咱们不整那些虚的,一步一步来,保证你能看懂,能上手,能做出东西来。
先把Echarts请进你的项目里
方式一:用CDN引入(最快上手)
如果你只是想快速试试手,或者做个小demo,用CDN引入是最省事的。你只需要在HTML文件里加一行代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的第一个Echarts图表</title>
<!-- 引入Echarts,CDN地址 -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
</head>
<body>
<!-- 图表容器,必须有宽度和高度 -->
<div id="main" style="width: 600px; height: 400px;"></div>
<script>
// 初始化Echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 配置项
var option = {
title: {
text: '第一个图表'
},
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五']
},
yAxis: {
type: 'value'
},
series: [{
data: [820, 932, 901, 934, 1290],
type: 'bar'
}]
};
// 把配置项给实例
myChart.setOption(option);
</script>
</body>
</html>
就这么简单,保存成.html文件,用浏览器打开,你就能看到一个柱状图了。
方式二:npm安装(正式项目推荐)
如果你的项目是用Vue、React或者Node.js来写的,那用npm安装会更规范一些:
npm install echarts --save
然后在你的JavaScript文件里引入:
// 完整引入
import * as echarts from 'echarts';
// 或者按需引入(推荐,减小打包体积)
import * as echarts from 'echarts/core';
import { BarChart } from 'echarts/charts';
import { TitleComponent, TooltipComponent, GridComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
// 注册必须的组件
echarts.use([BarChart, TitleComponent, TooltipComponent, GridComponent, CanvasRenderer]);
为什么要按需引入?你想啊,一个完整项目的Echarts可能有几百个图表类型和组件,你实际上可能只用一两个。全部打包进去,体积会很大,用户下载慢,你的项目也会臃肿。按需引入就像点外卖——你想吃什么点什么,不用把整个餐厅搬回家。
方式三:用Vue3来玩(现在最流行)
现在很多人用Vue3做项目,我也推荐你用Vue3来学Echarts。新建一个Vue项目:
npm create vue@latest
cd your-project-name
npm install echarts
然后创建一个LineChart.vue组件:
<template>
<div class="chart-container">
<div ref="chartRef" style="width: 100%; height: 400px;"></div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import * as echarts from 'echarts';
const chartRef = ref(null);
let chartInstance = null;
onMounted(() => {
// 初始化图表
chartInstance = echarts.init(chartRef.value);
// 配置项
const option = {
title: {
text: '月度销售额趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: {
type: 'value'
},
series: [
{
name: '销售额',
type: 'line',
data: [12000, 13200, 10100, 14300, 15000, 18000, 17500, 19000, 20000, 22000, 25000, 28000],
smooth: true,
areaStyle: {
opacity: 0.3
}
}
]
};
chartInstance.setOption(option);
});
// 组件销毁时销毁图表实例,避免内存泄漏
onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.dispose();
}
});
</script>
<style scoped>
.chart-container {
width: 100%;
padding: 20px;
}
</style>
Echarts的核心概念:你得先懂这些”名词”
Echarts的配置项里有很多专业术语,我第一次看的时候也是一头雾水。来,我一个个给你翻译成人话:
1. 实例(Instance)
实例就是你手里的”画布”。你得先创建一个实例,然后才能往上面画图。echarts.init()就是创建实例的方法,它需要一个容器元素作为参数。
// 创建实例
var chart = echarts.init(document.getElementById('container'));
2. 配置项(Option)
配置项是一个大对象,里面包含了图表的所有信息——标题、坐标轴、数据、颜色、样式等等。setOption()方法就是把这些配置项”告诉”实例,让它按照你的要求去画图。
var option = {
// 这里写各种配置
};
chart.setOption(option);
3. 组件(Component)
Echarts的组件就是图表的各种”零件”:标题(title)、提示框(tooltip)、图例(legend)、坐标轴(xAxis/yAxis)、数据集(dataset)等等。你可以把组件理解为乐高积木,你可以根据需要自由组合。
4. 系列(Series)
系列是真正画数据的”画笔”。一个图表可以有多个系列,每个系列有类型(柱状图、折线图、饼图等)和数据。这是Echarts最核心的概念。
series: [
{
type: 'bar', // 柱状图
name: '销售额',
data: [100, 200, 300]
},
{
type: 'line', // 折线图
name: '利润',
data: [50, 100, 150]
}
]
先搞定最基础的五种图表
柱状图(Bar Chart)
柱状图是最常用的图表之一,用来比较不同类别的数据大小。
var option = {
title: {
text: '各部门季度销售额',
left: 'center',
textStyle: {
fontSize: 18,
fontWeight: 'bold'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['Q1', 'Q2', 'Q3', 'Q4']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['技术部', '销售部', '市场部', '运营部', '人力资源部']
},
yAxis: {
type: 'value',
name: '销售额(万元)'
},
series: [
{
name: 'Q1',
type: 'bar',
data: [120, 200, 150, 80, 70],
itemStyle: {
color: '#5470c6'
}
},
{
name: 'Q2',
type: 'bar',
data: [130, 220, 160, 90, 80],
itemStyle: {
color: '#91cc75'
}
},
{
name: 'Q3',
type: 'bar',
data: [140, 210, 170, 95, 85],
itemStyle: {
color: '#fac858'
}
},
{
name: 'Q4',
type: 'bar',
data: [160, 240, 180, 110, 90],
itemStyle: {
color: '#ee6666'
}
}
]
};
关键点解释:
grid控制图表的边距,containLabel: true确保标签不会被裁剪tooltip.trigger: 'axis'让鼠标悬停时显示整轴的数据itemStyle.color可以自定义每个柱子的颜色- 如果想让柱子之间有间隔,可以在series里加
barGap: '20%'
折线图(Line Chart)
折线图适合展示数据随时间变化的趋势。
var option = {
title: {
text: '2024年用户增长趋势',
subtext: '单位:万人',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['新增用户', '活跃用户', '付费用户'],
bottom: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false, // 折线图边界不留白
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: {
type: 'value',
name: '用户数'
},
series: [
{
name: '新增用户',
type: 'line',
data: [5, 8, 12, 15, 18, 22, 25, 30, 35, 40, 45, 52],
smooth: true, // 平滑曲线
areaStyle: {
opacity: 0.2
},
lineStyle: {
width: 3
}
},
{
name: '活跃用户',
type: 'line',
data: [30, 35, 42, 48, 55, 62, 70, 78, 85, 92, 100, 110],
smooth: true,
lineStyle: {
width: 3,
type: 'dashed' // 虚线
}
},
{
name: '付费用户',
type: 'line',
data: [3, 5, 8, 10, 13, 16, 20, 24, 28, 32, 38, 45],
smooth: true,
lineStyle: {
width: 3
}
}
]
};
关键点解释:
boundaryGap: false折线图的X轴数据点会贴在坐标轴上,而不是有间隔smooth: true让折线变得平滑,像曲线一样areaStyle.opacity可以给折线图下面加一个渐变填充,看起来很高级lineStyle.type: 'dashed'可以画虚线
饼图(Pie Chart)
饼图用来展示各部分占整体的比例。
var option = {
title: {
text: '网站流量来源分析',
subtext: '2024年数据',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)' // 自定义提示框内容
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center'
},
series: [
{
name: '流量来源',
type: 'pie',
radius: ['40%', '70%'], // 环形图,不是实心饼图
center: ['60%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}\n{c} ({d}%)',
lineHeight: 20
},
labelLine: {
show: true
},
emphasis: {
label: {
show: true,
fontSize: 16,
fontWeight: 'bold'
}
},
data: [
{ value: 1048, name: '搜索引擎' },
{ value: 735, name: '直接访问' },
{ value: 580, name: '邮件营销' },
{ value: 484, name: '联盟广告' },
{ value: 300, name: '视频广告' }
]
}
]
};
关键点解释:
radius: ['40%', '70%']设置内外半径,可以做出环形图(甜甜圈图)formatter用{a}表示系列名,{b}表示数据名,{c}表示数值,{d}表示百分比itemStyle.borderRadius让饼图的分块有圆角,看起来更柔和emphasis设置鼠标悬停时的高亮效果
散点图(Scatter Chart)
散点图用来展示两个变量之间的关系,适合做相关性分析。
var option = {
title: {
text: '广告投入与销售额关系分析',
subtext: '数据点越大表示销售额越高'
},
tooltip: {
trigger: 'item',
formatter: function(params) {
return `广告投入:${params.value[0]}万元<br/>销售额:${params.value[1]}万元`;
}
},
xAxis: {
type: 'value',
name: '广告投入(万元)'
},
yAxis: {
type: 'value',
name: '销售额(万元)'
},
series: [
{
type: 'scatter',
data: [
[10, 50], [20, 90], [30, 140], [40, 180], [50, 230],
[60, 270], [70, 320], [80, 360], [90, 410], [100, 450],
[15, 60], [25, 110], [35, 160], [45, 200], [55, 250],
[65, 290], [75, 340], [85, 380], [95, 430], [105, 470]
],
symbolSize: function(data) {
// 根据销售额调整散点大小
return Math.sqrt(data[1]) * 2;
},
itemStyle: {
color: function(params) {
// 根据销售额给不同颜色
var value = params.value[1];
if (value > 400) return '#ee6666';
if (value > 250) return '#fac858';
return '#5470c6';
}
}
}
]
};
关键点解释:
- 散点图的数据格式是二维数组
[x, y] symbolSize可以是一个函数,根据数据动态调整散点大小itemStyle.color也可以是函数,根据数据动态调整颜色- 这种动态配置让散点图非常灵活,可以用来做气泡图
雷达图(Radar Chart)
雷达图适合多维度对比分析,比如评估一个员工的各项能力。
var option = {
title: {
text: '团队成员能力评估雷达图',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
data: ['张三', '李四'],
bottom: 10
},
radar: {
indicator: [
{ name: '技术能力', max: 100 },
{ name: '沟通能力', max: 100 },
{ name: '团队协作', max: 100 },
{ name: '创新能力', max: 100 },
{ name: '执行力', max: 100 },
{ name: '学习能力', max: 100 }
],
center: ['50%', '50%'],
radius: '65%'
},
series: [
{
name: '能力评估',
type: 'radar',
data: [
{
value: [90, 75, 85, 80, 88, 92],
name: '张三',
itemStyle: { color: '#5470c6' },
areaStyle: { opacity: 0.3 }
},
{
value: [70, 90, 78, 85, 75, 80],
name: '李四',
itemStyle: { color: '#91cc75' },
areaStyle: { opacity: 0.3 }
}
]
}
]
};
关键点解释:
radar.indicator定义了雷达图的各个维度及其最大值- 每个数据点是一个数组,按indicator的顺序对应各个维度的值
areaStyle.opacity给雷达图区域加半透明填充,两个区域重叠时能清楚看到
数据动态更新:让图表”活”起来
很多初学者只会在页面上画一个静态图表,但实际上,真实项目中的数据都是动态变化的。我来教你怎么实现数据动态更新。
模拟实时数据
var chart = echarts.init(document.getElementById('main'));
var option = {
title: {
text: '实时温度监控',
subtext: '每秒更新一次'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value',
name: '温度(℃)',
min: 0,
max: 50
},
series: [
{
name: '温度',
type: 'line',
data: [],
smooth: true,
lineStyle: { width: 2 },
itemStyle: { color: '#ee6666' },
areaStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(238, 102, 102, 0.5)' },
{ offset: 1, color: 'rgba(238, 102, 102, 0.05)' }
]
}
}
}
]
};
chart.setOption(option);
// 模拟实时数据
var now = new Date();
var oneMinute = 60 * 1000;
var lastTime = +now;
var baseValue = Math.random() * 10;
setInterval(function() {
now = new Date();
// 数据格式:[时间, 数值]
var time = echarts.format.formatTime('yyyy-MM-dd HH:mm:ss', now);
baseValue += Math.random() * 2 - 1; // 随机波动
if (baseValue < 0) baseValue = 0;
if (baseValue > 50) baseValue = 50;
// 更新数据,保持最近10个点
var data = option.series[0].data;
data.push([time, parseFloat(baseValue.toFixed(1))]);
if (data.length > 10) {
data.shift();
}
option.xAxis.data = data.map(function(item) {
return item[0];
});
chart.setOption(option, true); // true表示不合并,直接替换
}, oneMinute);
关键点解释:
echarts.format.formatTime()是Echarts自带的格式化时间函数setInterval定时更新数据,模拟实时效果chart.setOption(option, true)第二个参数true表示不合并,直接替换配置- 保持数据量固定(
data.length > 10时删除最早的数据),避免内存无限增长
从API获取数据
真实项目中,数据一般来自后端API。下面是一个从API获取数据并渲染图表的例子:
async function loadChart() {
try {
// 假设后端API返回数据
const response = await fetch('/api/sales-data');
const result = await response.json();
// 解析数据
const categories = result.categories; // ['周一', '周二', ...]
const salesData = result.sales; // [1200, 1350, ...]
const profitData = result.profit; // [300, 450, ...]
// 更新图表
myChart.setOption({
xAxis: {
data: categories
},
series: [
{
name: '销售额',
data: salesData
},
{
name: '利润',
data: profitData
}
]
});
} catch (error) {
console.error('加载数据失败:', error);
// 显示错误提示
myChart.showLoading({
text: '数据加载失败',
color: '#ee6666'
});
}
}
// 页面加载时获取数据
loadChart();
关键点解释:
- 用
async/await处理异步请求,代码更简洁 - 如果请求失败,可以用
showLoading()显示错误提示 - 实际项目中,建议加上 loading 状态,让用户知道图表正在加载
响应式适配:图表要适应各种屏幕
很多新手在写图表的时候,给容器定死了宽高,结果在手机上查看就变形了。Echarts提供了很好的响应式支持:
var chart = echarts.init(document.getElementById('main'));
// 初始配置
var option = {
title: { text: '响应式图表示例' },
xAxis: { type: 'category', data: ['A', 'B', 'C', 'D', 'E'] },
yAxis: { type: 'value' },
series: [{ type: 'bar', data: [120, 200, 150, 80, 70] }]
};
chart.setOption(option);
// 监听窗口大小变化,自动调整图表
window.addEventListener('resize', function() {
chart.resize();
});
// 或者用更优雅的方式:在Vue3中
// window.addEventListener('resize', handleResize);
// window.addEventListener('beforeunload', handleBeforeUnload);
function handleResize() {
chart.resize();
}
function handleBeforeUnload() {
window.removeEventListener('resize', handleResize);
}
关键点解释:
chart.resize()让图表根据容器大小自动调整- 在
window.addEventListener('resize', ...)中调用,确保窗口大小变化时图表也会跟着变 - 记得在组件销毁时移除事件监听,避免内存泄漏
高级技巧:让图表更好看、更好用
自定义颜色主题
Echarts内置了几个主题,你也可以自定义:
// 使用内置主题
var chart = echarts.init(document.getElementById('main'), 'dark'); // 'dark' 主题
// 或者自定义主题
var myTheme = {
color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272'],
backgroundColor: 'transparent',
title: {
textStyle: { fontSize: 18, color: '#333' }
},
tooltip: {
backgroundColor: 'rgba(50,50,50,0.8)',
textStyle: { color: '#fff' }
}
};
var chart2 = echarts.init(document.getElementById('main2'), myTheme);
图表联动:点击一个图表,另一个跟着变
有时候你需要多个图表联动展示数据,比如点击柱状图的某个柱子,折线图显示对应的详情。
// 主图表(柱状图)
var mainChart = echarts.init(document.getElementById('main'));
var detailChart = echarts.init(document.getElementById('detail'));
var option = {
title: { text: '各产品销售额' },
xAxis: { type: 'category', data: ['产品A', '产品B', '产品C', '产品D'] },
yAxis: { type: 'value' },
series: [{ type: 'bar', data: [1200, 2000, 1500, 800] }]
};
mainChart.setOption(option);
// 详情图表(折线图)
var detailOption = {
title: { text: '产品A的月度销售' },
xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
yAxis: { type: 'value' },
series: [{ type: 'line', data: [100, 150, 200, 180, 220, 250] }]
};
detailChart.setOption(detailOption);
// 点击柱状图,更新折线图
mainChart.on('click', function(params) {
var productName = params.name;
// 根据点击的产品,获取对应的数据(实际项目中从API获取)
var detailData = getDetailData(productName);
detailChart.setOption({
title: { text: productName + '的月度销售' },
series: [{ data: detailData }]
});
});
function getDetailData(productName) {
// 模拟数据
var dataMap = {
'产品A': [100, 150, 200, 180, 220, 250],
'产品B': [200, 250, 300, 280, 320, 350],
'产品C': [150, 180, 220, 200, 240, 280],
'产品D': [80, 100, 120, 110, 130, 150]
};
return dataMap[productName] || [];
}
关键点解释:
mainChart.on('click', ...)监听点击事件params.name获取点击的数据项名称- 根据点击的项,动态更新另一个图表的数据
图表导出为图片
很多项目需要把图表导出为图片,方便分享或打印:
// 点击按钮导出图片
document.getElementById('exportBtn').addEventListener('click', function() {
var url = chart.getDataURL({
type: 'png', // 导出格式:png 或 jpeg
pixelRatio: 2, // 像素比,2表示高清
backgroundColor: '#fff' // 背景色
});
// 触发下载
var a = document.createElement('a');
a.href = url;
a.download = '图表-' + new Date().getTime() + '.png';
a.click();
});
// 或者导出为Base64字符串
var base64 = chart.getConnectedDataURL({
type: 'png',
pixelRatio: 2
});
常见坑和问题解决
坑1:图表容器没有设置宽高
这是新手最容易犯的错误。Echarts的图表容器必须有明确的宽度和高度,否则图表无法正常显示。
<!-- ❌ 错误:没有设置宽高 -->
<div id="main"></div>
<!-- ✅ 正确:设置宽高 -->
<div id="main" style="width: 600px; height: 400px;"></div>
<!-- ✅ 或者用CSS -->
<style>
#main {
width: 100%;
height: 400px;
}
</style>
坑2:忘记初始化就调用方法
// ❌ 错误:chart还没初始化就用
chart.setOption(option);
// ✅ 正确:先初始化
var chart = echarts.init(document.getElementById('main'));
chart.setOption(option);
坑3:组件销毁时忘记销毁图表实例
// ❌ 错误:组件销毁时没有销毁图表实例
onBeforeUnmount(() => {
// 啥也没做
});
// ✅ 正确:销毁图表实例
onBeforeUnmount(() => {
chart.dispose();
});
不销毁实例会导致内存泄漏,页面长时间运行后会越来越卡。
坑4:数据更新时没有正确处理
// ❌ 错误:直接修改数据,没有调用setOption
option.series[0].data.push(100);
// ✅ 正确:修改数据后调用setOption
option.series[0].data.push(100);
chart.setOption(option);
坑5:X轴数据太长导致标签重叠
// ❌ 数据太多,标签重叠
xAxis: {
type: 'category',
data: ['一月', '二月', '三月', '四月', '五月', '六月',
'七月', '八月', '九月', '十月', '十一月', '十二月']
}
// ✅ 解决方案1:倾斜标签
xAxis: {
type: 'category',
data: [...],
axisLabel: {
rotate: 45 // 旋转45度
}
}
// ✅ 解决方案2:隔一个显示一个
xAxis: {
type: 'category',
data: [...],
axisLabel: {
interval: 1 // 隔1个显示1个
}
}
// ✅ 解决方案3:使用数据缩放(适合数据非常多的情况)
xAxis: {
type: 'category',
data: [...],
anchor: { show: true }
},
dataZoom: [
{ type: 'inside', start: 0, end: 100 },
{ type: 'slider', start: 0, end: 100 }
]
实战项目:做一个完整的仪表盘
光看不练假把式,咱们来做一个完整的仪表盘项目。假设你要做一个销售数据仪表盘,包含:
- 顶部KPI卡片(销售额、订单数、客户数、转化率)
- 销售趋势折线图
- 各地区销售占比饼图
- 产品销量排行榜柱状图
<template>
<div class="dashboard">
<h1>销售数据仪表盘</h1>
<!-- KPI卡片区域 -->
<div class="kpi-cards">
<div class="kpi-card" v-for="(card, index) in kpiData" :key="index">
<div class="kpi-title">{{ card.title }}</div>
<div class="kpi-value">{{ card.value }}</div>
<div class="kpi-change" :class="card.change > 0 ? 'up' : 'down'">
{{ card.change > 0 ? '↑' : '↓' }} {{ Math.abs(card.change) }}%
</div>
</div>
</div>
<!-- 图表区域 -->
<div class="charts">
<!-- 销售趋势图 -->
<div class="chart-box">
<div class="chart-title">销售趋势</div>
<div ref="trendChartRef" class="chart"></div>
</div>
<!-- 地区占比图 -->
<div class="chart-box">
<div class="chart-title">各地区销售占比</div>
<div ref="pieChartRef" class="chart"></div>
</div>
</div>
<!-- 排行榜 -->
<div class="chart-box">
<div class="chart-title">产品销量排行榜</div>
<div ref="rankChartRef" class="chart"></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts';
// KPI数据
const kpiData = ref([
{ title: '总销售额', value: '¥1,234,567', change: 12.5 },
{ title: '订单数', value: '8,901', change: 8.3 },
{ title: '客户数', value: '3,456', change: -2.1 },
{ title: '转化率', value: '4.56%', change: 1.2 }
]);
// 图表引用
const trendChartRef = ref(null);
const pieChartRef = ref(null);
const rankChartRef = ref(null);
// 图表实例
let trendChart, pieChart, rankChart;
onMounted(() => {
initCharts();
});
function initCharts() {
// 销售趋势折线图
trendChart = echarts.init(trendChartRef.value);
trendChart.setOption({
tooltip: { trigger: 'axis' },
legend: { data: ['销售额', '利润'], bottom: 0 },
grid: { left: '3%', right: '4%', bottom: '15%', containLabel: true },
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: { type: 'value' },
series: [
{
name: '销售额',
type: 'line',
data: [12000, 13200, 10100, 14300, 15000, 18000, 17500, 19000, 20000, 22000, 25000, 28000],
smooth: true,
areaStyle: { opacity: 0.2 },
itemStyle: { color: '#5470c6' }
},
{
name: '利润',
type: 'line',
data: [3000, 3500, 2800, 4000, 4200, 5000, 4800, 5200, 5500, 6000, 6500, 7000],
smooth: true,
itemStyle: { color: '#91cc75' }
}
]
});
// 地区占比饼图
pieChart = echarts.init(pieChartRef.value);
pieChart.setOption({
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
legend: { orient: 'vertical', right: 10, top: 'center' },
series: [
{
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: { show: false },
emphasis: {
label: { show: true, fontSize: 14, fontWeight: 'bold' }
},
data: [
{ value: 1048, name: '华东' },
{ value: 735, name: '华南' },
{ value: 580, name: '华北' },
{ value: 484, name: '西部' },
{ value: 300, name: '东北' }
]
}
]
});
// 产品销量排行榜
rankChart = echarts.init(rankChartRef.value);
rankChart.setOption({
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: { type: 'value' },
yAxis: {
type: 'category',
data: ['产品E', '产品D', '产品C', '产品B', '产品A']
},
series: [
{
type: 'bar',
data: [320, 450, 580, 720, 900],
itemStyle: {
color: function(params) {
var colors = ['#ee6666', '#fac858', '#91cc75', '#5470c6', '#73c0de'];
return colors[params.dataIndex];
}
},
label: {
show: true,
position: 'right'
}
}
]
});
// 响应式适配
window.addEventListener('resize', handleResize);
}
function handleResize() {
trendChart && trendChart.resize();
pieChart && pieChart.resize();
rankChart && rankChart.resize();
}
// 组件销毁时清理
import { onBeforeUnmount } from 'vue';
onBeforeUnmount(() => {
trendChart && trendChart.dispose();
pieChart && pieChart.dispose();
rankChart && rankChart.dispose();
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped>
.dashboard {
padding: 20px;
max-width: 1400px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 30px;
color: #333;
}
.kpi-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 30px;
}
.kpi-card {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
text-align: center;
}
.kpi-title {
color: #999;
font-size: 14px;
margin-bottom: 10px;
}
.kpi-value {
font-size: 28px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.kpi-change {
font-size: 14px;
}
.kpi-change.up { color: #52c41a; }
.kpi-change.down { color: #ee6666; }
.charts {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.chart-box {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
}
.chart-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.chart {
width: 100%;
height: 300px;
}
</style>
关键点解释:
- 用Vue3的
<script setup>语法,代码更简洁 - 三个图表分别初始化,各自管理自己的生命周期
- 响应式适配用
window.addEventListener('resize', ...)监听 - 组件销毁时用
dispose()清理图表实例,避免内存泄漏 - KPI卡片用CSS Grid布局,自动适配不同屏幕宽度
Echarts的进阶玩法
自定义系列:做个漏斗图
Echarts除了内置的图表类型,还支持自定义系列。比如漏斗图:
var option = {
title: {
text: '销售漏斗分析',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%'
},
series: [
{
name: '漏斗图',
type: 'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending',
gap: 2,
label: {
show: true,
position: 'inside'
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid'
}
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1
},
emphasis: {
label: {
fontSize: 20
}
},
data: [
{ value: 100, name: '访客' },
{ value: 80, name: '浏览商品' },
{ value: 60, name: '加入购物车' },
{ value: 40, name: '提交订单' },
{ value: 20, name: '支付成功' }
]
}
]
};
地图图表
Echarts还支持地图,可以展示各地区的销售数据:
// 先注册中国地图
fetch('https://raw.githubusercontent.com/apache/echarts-examples/main/public/data/asset/geo/china.json')
.then(res => res.json())
.then(geoJson => {
echarts.registerMap('china', geoJson);
var chart = echarts.init(document.getElementById('mapChart'));
var option = {
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{c} 万元'
},
visualMap: {
min: 0,
max: 5000,
left: 'left',
top: 'bottom',
text: ['高', '低'],
calculable: true,
inRange: {
color: ['e0f3f8', '#006090']
}
},
series: [
{
name: '各地区销售额',
type: 'map',
map: 'china',
roam: true, // 允许缩放和平移
zoom: 1.2,
label: {
show: true
},
emphasis: {
label: {
fontSize: 12,
fontWeight: 'bold'
}
},
data: [
{ name: '广东', value: 4500 },
{ name: '江苏', value: 3800 },
{ name: '浙江', value: 3500 },
{ name: '山东', value: 2800 },
{ name: '河南', value: 2200 },
{ name: '四川', value: 1800 },
{ name: '湖北', value: 1500 },
{ name: '湖南', value: 1200 },
{ name: '安徽', value: 1000 },
{ name: '福建', value: 900 }
]
}
]
};
chart.setOption(option);
});
关键点解释:
- 地图数据需要单独加载GeoJSON文件
visualMap是视觉映射组件,根据数值大小自动分配颜色roam: true允许用户缩放和平移地图series.data是各地区的销售额数据
图表动画效果
Echarts的图表默认就有动画效果,但你也可以自定义:
var option = {
animation: true, // 开启动画
animationDuration: 1000, // 动画持续时间(毫秒)
animationEasing: 'cubicOut', // 动画缓动效果
series: [
{
type: 'bar',
data: [120, 200, 150, 80, 70],
// 每个柱子的动画延迟不同,产生依次显示的效果
animationDelay: function(idx) {
return idx * 100;
}
}
]
};
常用的 animationEasing 值有:linear、cubicIn、cubicOut、cubicInOut、quadraticIn、quadraticOut、quadraticInOut、quarticIn、quarticOut、quarticInOut、exponentialIn、exponentialOut、exponentialInOut、cosineIn、cosineOut、cosineInOut、elasticIn、elasticOut、elasticInOut、backIn、backOut、backInOut、bounceIn、bounceOut、bounceInOut。
学习路径建议
如果你是完全零基础,我建议按这个顺序来学:
- 先搞懂基本概念:实例、配置项、组件、系列。这四个概念搞清楚了,Echarts就理解了80%。
- 从柱状图开始:柱状图是最简单直观的图表,先练手。
- 然后学折线图:折线图常用于展示趋势,很常用。
- 再学饼图和散点图:这两种图表有各自的适用场景。
- 最后学雷达图和地图:这两种图表相对复杂,但应用场景也很独特。
- 动手做个完整项目:光看不练假把式,做个仪表盘或者数据看板,把所有知识点串起来。
一些实用的小技巧
1. 用dataset简化数据管理
当你的数据量和系列都比较多的时候,用 dataset 管理数据会更清晰:
var 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],
['Walnut Brownie', 72.4, 53.9, 39.1]
]
},
{
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],
['Walnut Brownie', 72.4, 53.9, 39.1]
],
transform: {
type: 'sort',
config: { dimension: 2, order: 'desc' }
}
}
],
xAxis: [
{ type: 'category', gridIndex: 0 },
{ type: 'category', gridIndex: 1 }
],
yAxis: [
{ gridIndex: 0 },
{ gridIndex: 1 }
],
grid: [
{ bottom: '55%' },
{ top: '55%' }
],
series: [
{ type: 'bar', datasetIndex: 0 },
{ type: 'bar', datasetIndex: 1 }
]
};
dataset 的优势在于数据和配置分离,数据可以方便地更新,而不用改配置项。transform 可以对数据进行转换,比如排序、过滤等。
2. 用action控制图表
Echarts支持用 dispatchAction 来动态控制图表:
// 触发tooltip显示
chart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: 3
});
// 触发legend选中
chart.dispatchAction({
type: 'legendSelect',
name: '销售额'
});
// 触发dataZoom缩放
chart.dispatchAction({
type: 'dataZoom',
start: 0,
end: 50
});
这个功能在自动化测试、图表联动、定时触发等方面很有用。
3. 图表主题配色方案
给图表配一个好的颜色方案,能大大提升视觉效果。下面是一些常用的配色:
// 商务风格
var businessTheme = {
color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4']
};
// 清新风格
var freshTheme = {
color: ['#00d4aa', '#00b8d4', '#5c6bc0', '#ab47bc', '#ff7043', '#66bb6a', '#ffa726', '#ef5350']
};
// 深色风格
var darkTheme = {
color: ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#742f30', '#476b93', '#e2d8b3']
};
选配色方案的时候,要考虑图表的使用场景。商务报告适合稳重的颜色,创意展示可以用更鲜艳的颜色。
最后说几句
学了这么多种图表,你可能觉得有点多。但别担心,你不需要全部都记住。Echarts的强大之处在于,你只需要掌握核心概念,然后遇到问题时去查文档就好。
记住这三件事:
- 创建一个实例:
echarts.init(container) - 写好配置项:
option对象 - 调用setOption:
chart.setOption(option)
这就够了。其他的都是配置项的具体写法,多看多练就熟悉了。
Echarts的文档做得非常完善,几乎每个配置项都有示例:https://echarts.apache.org/zh/index.html
遇到问题的时候,先在文档里搜索,基本都能找到答案。如果找不到,可以去GitHub提issue,或者在社区里提问,Echarts的社区非常活跃。
最后,送你一句话:“看懂了不代表会做了,会做了不代表能做好。” 学到的东西,一定要动手去练。光看教程,永远学不会游泳。打开编辑器,写代码,跑起来,看到图表出现在屏幕上,那种成就感是无可替代的。
加油,祝你在数据可视化的路上越走越远!
