像拼图一样搭出数据地图 Echarts视频从入门到实战 教你画出动态曲线图和饼图 解决图表显示空白和样式错乱问题
一、为什么你需要一张”会说话”的图?
你想象一下这个场景:
你做了三个月的数据分析,终于跑出了一份漂亮的报表。结果汇报的时候,领导盯着表格看了三秒,说:”你这能看懂吗?”
你心里一万只草泥马奔腾而过。
数据可视化这件事,从来不是”画个图”那么简单。好的图表,是让数据自己开口说话。一条动态曲线,能一眼看出趋势;一个饼图,能立刻分清主次。
而Echarts,就是让这件事变得简单的神器。
二、Echarts到底是什么?
先别被名字吓到。
Echarts(读作/ˈiːkɑːrts/)是百度开源的一个纯JavaScript图表库。说白了,就是一堆别人写好的代码,你拿过来调一下参数,图表就出来了。
它的特点是:
- 免费开源,商用也没问题
- 文档齐全,中文社区活跃
- 功能强大,支持的图表类型远超你的想象
- 兼容性好,PC、移动端都能用
现在市面上90%以上的国内项目,用的都是Echarts。你打开任何一个数据大屏、后台系统,大概率都能看到它的身影。
三、动态曲线图:让数据”动起来”
3.1 先搭框架
我们从一个最简单的例子开始。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>动态曲线图</title>
<style>
/* 关键:必须给容器指定宽高,否则图表不显示! */
#main {
width: 800px;
height: 400px;
margin: 50px auto;
}
</style>
</head>
<body>
<div id="main"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script>
// 初始化Echarts实例
const chart = echarts.init(document.getElementById('main'));
// 配置项
const option = {
// 标题
title: {
text: '月度销售趋势',
left: 'center'
},
// 提示框(鼠标悬停显示详情)
tooltip: {
trigger: 'axis'
},
// 图例
legend: {
data: ['销售额', '利润']
},
// X轴
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月']
},
// Y轴
yAxis: {
type: 'value'
},
// 数据系列
series: [
{
name: '销售额',
type: 'line',
data: [12000, 15000, 13000, 18000, 20000, 22000],
// 填充区域,让曲线更直观
areaStyle: {}
},
{
name: '利润',
type: 'line',
data: [3000, 4000, 3500, 5000, 6000, 7000]
}
]
};
// 渲染图表
chart.setOption(option);
</script>
</body>
</html>
3.2 让它”动”起来
静态图看完了,我们让它真正”活”起来。
下面是动态曲线图的完整代码,模拟实时数据刷新:
const chart = echarts.init(document.getElementById('main'));
// 初始数据
let xData = ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00'];
let yData = [120, 200, 150, 80, 70, 110];
const option = {
title: { text: '实时温度监控', left: 'center' },
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: xData,
boundaryGap: false // 曲线从Y轴开始,不是从中间
},
yAxis: {
type: 'value',
min: 0,
max: 250
},
series: [{
name: '温度',
type: 'line',
data: yData,
smooth: true, // 平滑曲线
symbol: 'circle', // 数据点样式
lineStyle: {
color: '#5470c6',
width: 3
}
}]
};
chart.setOption(option);
// 模拟实时数据刷新(每2秒更新一次)
setInterval(() => {
// 移除第一个数据
xData.shift();
yData.shift();
// 添加新数据
const now = new Date();
const timeStr = now.getHours() + ':' + String(now.getMinutes()).padStart(2, '0');
xData.push(timeStr);
yData.push(Math.floor(Math.random() * 100) + 80); // 随机温度
// 更新图表
chart.setOption({
xAxis: { data: xData },
series: [{ data: yData }]
});
}, 2000);
小贴士:动态图表的核心就是
setOption。每次调用它,Echarts会做增量更新,而不是重新渲染整张图,性能开销很小。
四、饼图:分清”谁是大头”
饼图是展示占比关系最直观的图表。
const pieChart = echarts.init(document.getElementById('pie-main'));
const pieOption = {
title: {
text: '用户来源分布',
left: 'center',
textStyle: { fontSize: 18 }
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)' // 显示名称、数值、百分比
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center'
},
series: [{
name: '用户来源',
type: 'pie',
radius: ['40%', '70%'], // 内径40%,外径70%,做成环形图更高级
center: ['50%', '55%'], // 位置偏移
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}\n{d}%'
},
emphasis: {
label: { show: true, fontSize: 16, fontWeight: 'bold' }
},
data: [
{ value: 1048, name: '搜索引擎' },
{ value: 735, name: '直接访问' },
{ value: 580, name: '邮件营销' },
{ value: 484, name: '联盟广告' },
{ value: 300, name: '视频广告' }
]
}]
};
pieChart.setOption(pieOption);
饼图有几个必记的坑,后面会细说。
五、常见”踩坑”现场:图表显示空白?样式错乱?
这部分是我最想跟你聊聊的。新手90%的问题,都出在这里。
5.1 图表不显示,一片空白
这是最常见的”灵异事件”。原因通常有这几个:
① 容器没有设置宽高
<!-- 错误示范:没有宽高 -->
<div id="main"></div>
<!-- 正确示范:必须有宽高 -->
<div id="main" style="width: 600px; height: 400px;"></div>
记住:Echarts 不会自动给容器撑开高度。你必须在CSS里明确指定。
② DOM还没渲染完就初始化
// 错误:页面还没加载完,元素还不存在
const chart = echarts.init(document.getElementById('main'));
// 正确:等DOM加载完再初始化
document.addEventListener('DOMContentLoaded', function() {
const chart = echarts.init(document.getElementById('main'));
chart.setOption(option);
});
或者如果你用了框架(Vue/React),确保在 mounted / useEffect 里初始化。
③ 容器隐藏时初始化
// 错误:父元素 display:none,Echarts拿到的宽高是0
const chart = echarts.init(document.getElementById('tab-panel'));
chart.setOption(option);
// 正确:显示之后再初始化,或者用 resize
setTimeout(() => {
chart.resize();
}, 0);
④ CDN链接失效或版本冲突
检查一下你引用的Echarts脚本:
<!-- 推荐用稳定版,不要用latest,容易出BUG -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
5.2 样式错乱:颜色、字体、图例都对不上
这种情况通常是因为配置项写错了位置。
const option = {
// 正确:series里的itemStyle控制每条线的颜色
series: [{
name: '销售额',
type: 'line',
data: [120, 200, 150],
itemStyle: {
color: '#5470c6' // ✅ 正确位置
}
}],
// 错误示范:把itemStyle写在顶层
itemStyle: {
color: '#5470c6' // ❌ 放在这里没用!
}
};
5.3 饼图数据为0时崩溃
// 错误:数据为0,饼图直接不显示
data: [
{ value: 0, name: '其他' }
]
// 正确:加上 minAngle 属性,保证小数据也能显示
series: [{
type: 'pie',
minAngle: 5, // 最小角度5度,确保小块也能显示
data: [
{ value: 5, name: '其他' }
]
}]
5.4 中文乱码
如果你看到图表上的中文显示成”□□□”,大概率是字体问题:
/* 全局设置中文字体 */
body {
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
}
或者在Echarts配置里:
const option = {
textStyle: {
fontFamily: 'Microsoft YaHei'
},
// ...其他配置
};
六、进阶:让图表更”好看”
6.1 主题切换
Echarts内置了几套主题,你也可以自定义:
// 使用内置主题
const chart = echarts.init(document.getElementById('main'), 'dark');
// 或者自定义主题
const customTheme = {
color: ['#5470c6', '#91cc75', '#fac858', '#ee6666'],
backgroundColor: '#fff',
textStyle: { fontFamily: 'Microsoft YaHei' }
};
const chart = echarts.init(document.getElementById('main'), customTheme);
6.2 响应式适配
// 监听窗口大小变化,自动调整
window.addEventListener('resize', () => {
chart.resize();
});
6.3 组合图表(曲线+柱状)
const option = {
xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
yAxis: [
{ type: 'value', name: '销售额' },
{ type: 'value', name: '占比' }
],
series: [
{
name: '销售额',
type: 'bar',
data: [12000, 15000, 13000, 18000, 20000, 22000]
},
{
name: '增长率',
type: 'line',
yAxisIndex: 1, // 关键:使用第二个Y轴
data: [12, 15, 8, 20, 18, 25]
}
]
};
七、实战项目:做一个数据监控面板
来,我们做一个稍微完整一点的项目。模拟一个后台数据监控面板:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>数据监控面板</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #1a1a2e;
color: #eee;
font-family: 'Microsoft YaHei', sans-serif;
padding: 20px;
}
.panel {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
max-width: 1200px;
margin: 0 auto;
}
.card {
background: #16213e;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
}
.card h3 {
font-size: 14px;
color: #aaa;
margin-bottom: 10px;
}
.card .value {
font-size: 32px;
font-weight: bold;
color: #4fc3f7;
}
.chart-card {
grid-column: span 2;
}
#line-chart, #pie-chart {
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<h1 style="text-align:center; margin-bottom:30px;">📊 实时数据监控面板</h1>
<div class="panel">
<!-- 指标卡片 -->
<div class="card">
<h3>今日访问</h3>
<div class="value" id="visit">12,456</div>
</div>
<div class="card">
<h3>订单数</h3>
<div class="value" id="order">1,234</div>
</div>
<div class="card">
<h3>转化率</h3>
<div class="value" id="rate">3.2%</div>
</div>
<!-- 曲线图(占两列) -->
<div class="card chart-card">
<h3>实时流量趋势</h3>
<div id="line-chart"></div>
</div>
<!-- 饼图 -->
<div class="card">
<h3>流量来源分布</h3>
<div id="pie-chart"></div>
</div>
</div>
<script>
// 曲线图配置
const lineChart = echarts.init(document.getElementById('line-chart'));
const lineOption = {
backgroundColor: 'transparent',
textStyle: { color: '#eee' },
tooltip: { trigger: 'axis' },
legend: { data: ['访问', '订单'], textStyle: { color: '#aaa' }, top: 10 },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
data: [],
axisLine: { lineStyle: { color: '#333' } },
axisLabel: { color: '#aaa' }
},
yAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#333' } },
axisLabel: { color: '#aaa' },
splitLine: { lineStyle: { color: '#222' } }
},
series: [
{
name: '访问',
type: 'line',
smooth: true,
data: [],
areaStyle: { color: 'rgba(79,195,247,0.2)' },
lineStyle: { color: '#4fc3f7' }
},
{
name: '订单',
type: 'line',
smooth: true,
data: [],
areaStyle: { color: 'rgba(255,152,0,0.2)' },
lineStyle: { color: '#ff9800' }
}
]
};
// 填充初始数据
const hours = ['00:00','04:00','08:00','12:00','16:00','20:00','24:00'];
lineOption.xAxis.data = hours;
lineOption.series[0].data = [120, 80, 300, 800, 600, 900, 700];
lineOption.series[1].data = [30, 20, 80, 200, 150, 250, 180];
lineChart.setOption(lineOption);
// 饼图配置
const pieChart = echarts.init(document.getElementById('pie-chart'));
const pieOption = {
backgroundColor: 'transparent',
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
legend: { orient: 'vertical', right: '5%', top: 'center', textStyle: { color: '#aaa' } },
series: [{
type: 'pie',
radius: ['35%', '65%'],
center: ['40%', '50%'],
data: [
{ value: 335, name: '直接访问' },
{ value: 310, name: '搜索引擎' },
{ value: 234, name: '联盟广告' },
{ value: 135, name: '邮件营销' },
{ value: 1548, name: '其他' }
],
itemStyle: { borderRadius: 5, borderColor: '#16213e', borderWidth: 2 },
label: { show: false }
}]
};
pieChart.setOption(pieOption);
// 响应式
window.addEventListener('resize', () => {
lineChart.resize();
pieChart.resize();
});
// 模拟实时更新
setInterval(() => {
const newVisit = Math.floor(Math.random() * 500) + 400;
const newOrder = Math.floor(Math.random() * 200) + 100;
lineChart.setOption({
series: [
{ data: lineOption.series[0].data.slice(1).concat([newVisit]) },
{ data: lineOption.series[1].data.slice(1).concat([newOrder]) }
]
});
document.getElementById('visit').textContent = (12456 + newVisit).toLocaleString();
document.getElementById('order').textContent = (1234 + newOrder).toLocaleString();
}, 2000);
</script>
</body>
</html>
八、几个”老手才知道”的Tips
① 不要重复初始化
// ❌ 错误:每次点击都重新初始化,内存泄漏
btn.onclick = function() {
const chart = echarts.init(dom); // 每次new一个新的实例
chart.setOption(option);
};
// ✅ 正确:只初始化一次,复用实例
const chart = echarts.init(dom);
btn.onclick = function() {
chart.setOption(newOption); // 只更新配置
};
② dispose()释放资源
当组件要销毁时,记得清理:
chart.dispose(); // 彻底释放,避免内存泄漏
③ 按需引入,减小体积
如果你只需要几种图表类型:
// 只引入你需要的模块
import * as echarts from 'echarts/core';
import { LineChart } from 'echarts/charts';
import { GridComponent, TooltipComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([LineChart, GridComponent, TooltipComponent, CanvasRenderer]);
④ 性能优化
数据量大的时候,Echarts可能会卡顿。可以用这个技巧:
// 数据量超过500时,关闭抗锯齿,提升渲染性能
chart.setOption({
series: [{
// ...
progressive: 1000, // 渐进式渲染
progressiveThreshold: 500
}]
});
九、总结一下
我们今天聊了这么多,核心就几句话:
- Echarts = 配置项驱动。你只要会写配置,图表就出来了。
- 动态图表的核心是
setOption,不是重新初始化。 - 空白问题80%是容器没宽高,样式错乱80%是配置写错了位置。
- 图表不是为了好看,是为了让数据说话。
如果你跟着做了上面的例子,你现在已经能画出基础的曲线图和饼图了。后面的路,就是多练、多看官方文档、多踩坑。
Echarts的官网文档非常详细:https://echarts.apache.org/zh/index.html
遇到问题,直接Ctrl+F搜,十有八九有人遇到过同样的问题。
最后送你一句话:
好图表的标准,不是”花不花哨”,而是”能不能让人一眼看懂”。
去吧,让你的数据自己说话。
