ECharts图表教程从零开始手把手教你制作柱状图折线图饼图和雷达图包含真实项目案例和常见问题解决方案
认识ECharts,这个图表库有点东西
先说说我为什么推荐ECharts。2013年百度开源了这个东西,到现在已经成了国内最火的图表库之一。它的优点很多:文档写得相当清晰(对中文用户很友好)、支持图表类型多、性能不错、还能定制。
你不需要是前端大牛才能用它。只要你会写HTML、懂一点JavaScript,跟着做就能上手。
准备工作,先别急着写代码
在开始之前,你需要确认几件事:
基础要求
- 一个HTML文件(比如
index.html) - 一个现代浏览器(Chrome、Firefox、Edge都可以)
- 一个代码编辑器(VS Code、WebStorm、甚至记事本都行)
引入ECharts
方式一:CDN引入(最简单,适合学习)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>ECharts入门</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
</head>
<body>
<!-- 图表会渲染在这里 -->
</body>
</html>
方式二:npm安装(适合项目中使用)
npm install echarts --save
import * as echarts from 'echarts';
// 或者
const echarts = require('echarts');
准备一个容器
图表必须放在一个有明确尺寸的div里,这是新手最容易踩的坑:
<div id="myChart" style="width: 800px; height: 500px;"></div>
注意:宽高必须设置,否则图表无法渲染。
第一个图表,30秒搞定
初始化ECharts实例,设置配置项,就这两步:
// 1. 获取容器
var chartDom = document.getElementById('myChart');
// 2. 初始化实例
var myChart = echarts.init(chartDom);
// 3. 配置项
var option = {
title: {
text: '我的第一个ECharts图表'
},
tooltip: {},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line'
}]
};
// 4. 设置配置项
myChart.setOption(option);
刷新页面,一条折线图就出现了。是不是比想象中简单?
柱状图,数据对比的好帮手
柱状图适合展示不同类别之间的数据对比。
基础柱状图
var option = {
title: {
text: '2024年各部门销售额(万元)',
left: 'center'
},
tooltip: {
trigger: 'axis',
formatter: '{b}:{c}万元' // 自定义提示框内容
},
xAxis: {
type: 'category',
data: ['市场部', '技术部', '运营部', '销售部', '人事部', '财务部']
},
yAxis: {
type: 'value',
name: '销售额(万元)'
},
series: [{
name: '销售额',
type: 'bar',
data: [320, 280, 250, 480, 120, 150],
// 柱状图样式
itemStyle: {
color: '#5470c6',
borderRadius: [4, 4, 0, 0] // 圆角效果
},
// 数据标签
label: {
show: true,
position: 'top',
formatter: '{c}万'
}
}]
};
分组柱状图,多个系列对比
var option = {
title: {
text: '近六个月各部门业绩对比',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['A部门', 'B部门', 'C部门']
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月']
},
yAxis: {
type: 'value',
name: '业绩(万元)'
},
series: [
{
name: 'A部门',
type: 'bar',
data: [120, 132, 101, 134, 90, 230],
itemStyle: { color: '#5470c6' }
},
{
name: 'B部门',
type: 'bar',
data: [220, 182, 191, 234, 290, 330],
itemStyle: { color: '#91cc75' }
},
{
name: 'C部门',
type: 'bar',
data: [150, 232, 201, 154, 190, 330],
itemStyle: { color: '#fac858' }
}
]
};
带最大值标记的柱状图
var option = {
title: {
text: '月度销售额与目标对比',
left: 'center'
},
tooltip: {
trigger: 'axis',
formatter: function(params) {
var res = params[0].name + '<br/>';
params.forEach(function(item) {
res += item.marker + item.seriesName + ':' + item.value + '万元<br/>';
});
return res;
}
},
legend: {
data: ['实际销售额', '目标销售额']
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: {
type: 'value',
name: '销售额(万元)'
},
series: [
{
name: '实际销售额',
type: 'bar',
data: [820, 932, 901, 934, 1290, 1330, 1320, 1400, 1500, 1600, 1700, 1800],
itemStyle: {
color: function(params) {
// 根据数值大小动态变色
if (params.value >= 1500) return '#91cc75';
if (params.value >= 1200) return '#fac858';
return '#ee6666';
}
},
markLine: {
data: [
{ type: 'average', name: '平均值' }
],
label: { show: true }
}
},
{
name: '目标销售额',
type: 'bar',
data: [1000, 1000, 1000, 1000, 1200, 1200, 1200, 1300, 1300, 1400, 1400, 1500],
itemStyle: {
color: '#5470c6',
opacity: 0.5
},
barWidth: '30%'
}
]
};
折线图,趋势数据的首选
折线图适合展示数据随时间变化的趋势。
基础折线图
var option = {
title: {
text: '近一年网站访问量趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['PC端', '移动端']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true // 防止坐标轴标签被截断
},
xAxis: {
type: 'category',
boundaryGap: false, // 折线从y轴开始,而不是留空白
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: {
type: 'value',
name: '访问量(万次)',
axisLabel: {
formatter: '{value}万'
}
},
series: [
{
name: 'PC端',
type: 'line',
data: [820, 932, 901, 934, 1290, 1330, 1320, 1400, 1500, 1600, 1700, 1800],
smooth: true, // 平滑曲线
areaStyle: {
opacity: 0.3 // 填充区域
}
},
{
name: '移动端',
type: 'line',
data: [620, 732, 701, 734, 1090, 1130, 1120, 1200, 1300, 1400, 1500, 1600],
smooth: true,
areaStyle: {
opacity: 0.3
}
}
]
};
带标记点的折线图
var option = {
title: {
text: '产品A月度销量',
left: 'center'
},
tooltip: {
trigger: 'axis',
formatter: function(params) {
var data = params[0].data;
// 自定义提示框,显示月份、销量、环比
var环比 = data.ratio ? '+' + data.ratio + '%' : '首次';
return params[0].name + '<br/>' +
params[0].marker + '销量:' + data.value + '件<br/>' +
'环比:' + 环比;
}
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: {
type: 'value',
name: '销量(件)'
},
series: [{
name: '销量',
type: 'line',
data: [
{value: 1200, ratio: null},
{value: 1350, ratio: 12.5},
{value: 1280, ratio: -5.2},
{value: 1450, ratio: 13.3},
{value: 1600, ratio: 10.3},
{value: 1550, ratio: -3.1},
{value: 1700, ratio: 9.7},
{value: 1850, ratio: 8.8},
{value: 1900, ratio: 2.7},
{value: 2100, ratio: 10.5},
{value: 2050, ratio: -2.4},
{value: 2300, ratio: 12.2}
],
symbol: 'circle', // 标记点形状
symbolSize: 8, // 标记点大小
markPoint: {
data: [
{type: 'max', name: '最大值'},
{type: 'min', name: '最小值'}
]
},
markLine: {
data: [
{type: 'average', name: '平均值'}
]
}
}]
};
堆叠面积图
var option = {
title: {
text: '各类渠道流量占比趋势',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {
data: ['搜索引擎', '直接访问', '社交媒体', '外部链接', '邮件营销']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
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',
stack: 'total',
areaStyle: {},
emphasis: { focus: 'series' },
data: [320, 332, 301, 334, 390, 330, 320, 350, 380, 400, 420, 450]
},
{
name: '直接访问',
type: 'line',
stack: 'total',
areaStyle: {},
emphasis: { focus: 'series' },
data: [120, 132, 101, 134, 90, 230, 210, 250, 280, 300, 320, 350]
},
{
name: '社交媒体',
type: 'line',
stack: 'total',
areaStyle: {},
emphasis: { focus: 'series' },
data: [220, 182, 191, 234, 290, 330, 310, 350, 380, 400, 420, 450]
},
{
name: '外部链接',
type: 'line',
stack: 'total',
areaStyle: {},
emphasis: { focus: 'series' },
data: [150, 232, 201, 154, 190, 330, 410, 350, 380, 400, 420, 450]
},
{
name: '邮件营销',
type: 'line',
stack: 'total',
areaStyle: {},
emphasis: { focus: 'series' },
data: [320, 332, 301, 334, 390, 330, 320, 350, 380, 400, 420, 450]
}
]
};
饼图,占比数据的可视化
饼图适合展示各部分占总体的比例。
基础饼图
var option = {
title: {
text: '2024年市场份额分布',
subtext: '数据来源:市场调研',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{c}({d}%)' // 显示名称、数值、百分比
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center'
},
series: [
{
name: '市场份额',
type: 'pie',
radius: '50%',
data: [
{ value: 35, name: '公司A' },
{ value: 25, name: '公司B' },
{ value: 18, name: '公司C' },
{ value: 12, name: '公司D' },
{ value: 10, name: '其他' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
环形图,更现代的风格
var option = {
title: {
text: '用户来源分布',
subtext: '月度统计',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{c}人({d}%)'
},
legend: {
orient: 'horizontal',
bottom: '5%'
},
series: [
{
name: '用户来源',
type: 'pie',
radius: ['40%', '70%'], // 内径和外径,形成环形
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: '视频广告' }
]
}
]
};
南丁格尔图(玫瑰图)
var option = {
title: {
text: '各产品线销售占比',
subtext: '玫瑰图展示',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{c}万元({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center'
},
series: [
{
name: '销售占比',
type: 'pie',
radius: [20, 140],
center: ['50%', '50%'],
roseType: 'area', // 玫瑰图模式
itemStyle: {
borderRadius: 5
},
data: [
{ value: 40, name: '手机' },
{ value: 38, name: '电脑' },
{ value: 32, name: '平板' },
{ value: 30, name: '智能手表' },
{ value: 28, name: '耳机' },
{ value: 26, name: '充电器' },
{ value: 24, name: '数据线' },
{ value: 22, name: '保护壳' }
]
}
]
};
带中间文字的饼图
var option = {
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{c}人({d}%)'
},
legend: {
bottom: '5%'
},
series: [
{
name: '年龄分布',
type: 'pie',
radius: ['35%', '65%'],
center: ['50%', '45%'],
roseType: 'radius',
label: {
formatter: '{b}\n{d}%'
},
data: [
{ value: 15, name: '18-25岁' },
{ value: 25, name: '26-35岁' },
{ value: 30, name: '36-45岁' },
{ value: 20, name: '46-55岁' },
{ value: 10, name: '55岁以上' }
]
}
],
// 中间添加文字
graphic: [
{
type: 'text',
left: 'center',
top: '38%',
style: {
text: '总用户',
fontSize: 16,
fill: '#999'
}
},
{
type: 'text',
left: 'center',
top: '48%',
style: {
text: '1000人',
fontSize: 28,
fontWeight: 'bold',
fill: '#333'
}
}
]
};
雷达图,多维数据的全面展示
雷达图适合展示多个维度的数据对比。
基础雷达图
var option = {
title: {
text: '员工能力评估雷达图',
left: 'center'
},
tooltip: {},
legend: {
data: ['员工A', '员工B']
},
radar: {
indicator: [
{ name: '沟通能力', max: 100 },
{ name: '技术能力', max: 100 },
{ name: '团队协作', max: 100 },
{ name: '创新能力', max: 100 },
{ name: '执行力', max: 100 },
{ name: '领导力', max: 100 }
]
},
series: [{
name: '能力评估',
type: 'radar',
data: [
{
value: [85, 90, 78, 82, 88, 75],
name: '员工A'
},
{
value: [70, 85, 92, 78, 80, 88],
name: '员工B'
}
]
}]
};
填充区域的雷达图
var option = {
title: {
text: '产品性能对比雷达图',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
data: ['产品A', '产品B', '产品C'],
top: '10%'
},
radar: {
indicator: [
{ name: '速度', max: 100 },
{ name: '稳定性', max: 100 },
{ name: '易用性', max: 100 },
{ name: '安全性', max: 100 },
{ name: '扩展性', max: 100 },
{ name: '性价比', max: 100 }
],
center: ['50%', '58%'],
radius: '65%'
},
series: [{
name: '产品对比',
type: 'radar',
data: [
{
value: [90, 85, 88, 82, 80, 85],
name: '产品A',
itemStyle: { color: '#5470c6' },
areaStyle: { opacity: 0.3 }
},
{
value: [75, 90, 82, 90, 88, 78],
name: '产品B',
itemStyle: { color: '#91cc75' },
areaStyle: { opacity: 0.3 }
},
{
value: [82, 78, 95, 75, 92, 90],
name: '产品C',
itemStyle: { color: '#fac858' },
areaStyle: { opacity: 0.3 }
}
]
}]
};
多雷达图,对比不同对象
var option = {
title: {
text: '班级学生综合能力对比',
left: 'center'
},
tooltip: {},
legend: {
data: ['张三', '李四', '王五'],
top: '10%'
},
radar: {
indicator: [
{ name: '语文', max: 100 },
{ name: '数学', max: 100 },
{ name: '英语', max: 100 },
{ name: '物理', max: 100 },
{ name: '化学', max: 100 },
{ name: '生物', max: 100 }
]
},
series: [{
name: '学生成绩',
type: 'radar',
lineStyle: {
width: 2
},
symbol: 'circle',
symbolSize: 6,
data: [
{
value: [85, 92, 78, 88, 82, 90],
name: '张三'
},
{
value: [78, 85, 90, 82, 88, 80],
name: '李四'
},
{
value: [90, 78, 85, 92, 80, 88],
name: '王五'
}
]
}]
};
真实项目案例,把这些组合起来
下面是一个实际项目中可能用到的综合案例:电商数据监控大屏。
完整项目代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电商数据监控大屏</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #0a1628;
color: #fff;
padding: 20px;
}
.header {
text-align: center;
padding: 20px 0;
border-bottom: 2px solid #1e3a5f;
margin-bottom: 20px;
}
.header h1 {
font-size: 32px;
color: #00d4ff;
letter-spacing: 8px;
}
.header .time {
font-size: 14px;
color: #666;
margin-top: 8px;
}
.dashboard {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.card {
background: linear-gradient(135deg, #1a2a4a 0%, #0d1f3c 100%);
border-radius: 10px;
padding: 20px;
border: 1px solid #1e3a5f;
box-shadow: 0 4px 20px rgba(0, 212, 255, 0.1);
}
.card-title {
font-size: 16px;
color: #00d4ff;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #1e3a5f;
display: flex;
align-items: center;
gap: 8px;
}
.card-title::before {
content: '';
width: 4px;
height: 16px;
background: #00d4ff;
border-radius: 2px;
}
.chart {
width: 100%;
height: 280px;
}
.big-chart {
grid-column: span 2;
}
.stats {
display: flex;
justify-content: space-around;
padding: 15px 0;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #00d4ff;
}
.stat-label {
font-size: 12px;
color: #888;
margin-top: 5px;
}
.highlight {
color: #ff6b6b;
}
.success {
color: #51cf66;
}
.warning {
color: #fcc419;
}
</style>
</head>
<body>
<div class="header">
<h1>电商平台数据监控中心</h1>
<div class="time" id="currentTime"></div>
</div>
<div class="dashboard">
<!-- 核心指标卡片 -->
<div class="card">
<div class="card-title">核心指标概览</div>
<div class="stats">
<div class="stat-item">
<div class="stat-value" id="totalSales">¥1,234,567</div>
<div class="stat-label">今日销售额</div>
</div>
<div class="stat-item">
<div class="stat-value success" id="orderCount">8,923</div>
<div class="stat-label">订单数量</div>
</div>
<div class="stat-item">
<div class="stat-value warning" id="visitorCount">45,678</div>
<div class="stat-label">访问用户</div>
</div>
</div>
</div>
<!-- 实时销售额趋势(折线图) -->
<div class="card">
<div class="card-title">实时销售额趋势</div>
<div id="salesTrend" class="chart"></div>
</div>
<!-- 品类销售占比(饼图) -->
<div class="card">
<div class="card-title">品类销售占比</div>
<div id="categoryPie" class="chart"></div>
</div>
<!-- 各地区销售对比(柱状图) -->
<div class="card big-chart">
<div class="card-title">各地区销售对比</div>
<div id="regionBar" class="chart"></div>
</div>
<!-- 用户能力画像(雷达图) -->
<div class="card">
<div class="card-title">用户价值评估</div>
<div id="userRadar" class="chart"></div>
</div>
<!-- 多维度对比(雷达图) -->
<div class="card">
<div class="card-title">产品性能对比</div>
<div id="productRadar" class="chart"></div>
</div>
<!-- 月度销售趋势(折线图) -->
<div class="card big-chart">
<div class="card-title">近一年月度销售趋势</div>
<div id="monthlyTrend" class="chart"></div>
</div>
</div>
<script>
// 更新时间
function updateTime() {
const now = new Date();
const timeStr = now.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
document.getElementById('currentTime').textContent = timeStr;
}
updateTime();
setInterval(updateTime, 1000);
// 销售趋势折线图
var salesTrendChart = echarts.init(document.getElementById('salesTrend'));
var salesTrendOption = {
tooltip: {
trigger: 'axis',
formatter: '{b} {c}万元'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['0:00', '4:00', '8:00', '12:00', '16:00', '20:00', '24:00'],
axisLine: { lineStyle: { color: '#1e3a5f' } },
axisLabel: { color: '#888' }
},
yAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#1e3a5f' } },
axisLabel: { color: '#888' },
splitLine: { lineStyle: { color: '#1e3a5f', type: 'dashed' } }
},
series: [{
name: '销售额',
type: 'line',
data: [12, 8, 45, 89, 76, 92, 65],
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
color: '#00d4ff',
width: 3
},
areaStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(0, 212, 255, 0.3)' },
{ offset: 1, color: 'rgba(0, 212, 255, 0)' }
]
}
}
}]
};
salesTrendChart.setOption(salesTrendOption);
// 品类销售占比饼图
var categoryPieChart = echarts.init(document.getElementById('categoryPie'));
var categoryPieOption = {
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{c}万元({d}%)'
},
legend: {
orient: 'vertical',
right: '5%',
top: 'center',
textStyle: { color: '#888' }
},
series: [{
name: '品类销售',
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
borderColor: '#0a1628',
borderWidth: 2
},
label: {
show: false
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold',
color: '#fff'
}
},
data: [
{ value: 35, name: '电子产品', itemStyle: { color: '#5470c6' } },
{ value: 25, name: '服装鞋帽', itemStyle: { color: '#91cc75' } },
{ value: 18, name: '食品饮料', itemStyle: { color: '#fac858' } },
{ value: 12, name: '家居用品', itemStyle: { color: '#ee6666' } },
{ value: 10, name: '其他', itemStyle: { color: '#73c0de' } }
]
}]
};
categoryPieChart.setOption(categoryPieOption);
// 地区销售对比柱状图
var regionBarChart = echarts.init(document.getElementById('regionBar'));
var regionBarOption = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function(params) {
var res = params[0].name + '<br/>';
params.forEach(function(item) {
res += item.marker + item.seriesName + ':' + item.value + '万元<br/>';
});
return res;
}
},
legend: {
data: ['线上销售', '线下销售'],
textStyle: { color: '#888' },
top: '5%'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['华东', '华南', '华北', '华中', '西南', '西北', '东北'],
axisLine: { lineStyle: { color: '#1e3a5f' } },
axisLabel: { color: '#888' }
},
yAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#1e3a5f' } },
axisLabel: { color: '#888' },
splitLine: { lineStyle: { color: '#1e3a5f', type: 'dashed' } }
},
series: [
{
name: '线上销售',
type: 'bar',
data: [450, 380, 320, 280, 200, 120, 150],
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: '#00d4ff' },
{ offset: 1, color: '#0066cc' }
]
},
borderRadius: [4, 4, 0, 0]
},
barWidth: '30%'
},
{
name: '线下销售',
type: 'bar',
data: [280, 250, 220, 180, 150, 80, 100],
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: '#51cf66' },
{ offset: 1, color: '#2f9e44' }
]
},
borderRadius: [4, 4, 0, 0]
},
barWidth: '30%'
}
]
};
regionBarChart.setOption(regionBarOption);
// 用户价值评估雷达图
var userRadarChart = echarts.init(document.getElementById('userRadar'));
var userRadarOption = {
tooltip: {},
radar: {
indicator: [
{ name: '消费能力', max: 100 },
{ name: '活跃度', max: 100 },
{ name: '忠诚度', max: 100 },
{ name: '复购率', max: 100 },
{ name: '评价质量', max: 100 },
{ name: '社交影响力', max: 100 }
],
center: ['50%', '55%'],
radius: '65%',
axisName: { color: '#888' },
splitArea: { areaStyle: { color: ['rgba(30, 58, 95, 0.3)', 'rgba(30, 58, 95, 0.5)'] } },
axisLine: { lineStyle: { color: 'rgba(30, 58, 95, 0.8)' } },
splitLine: { lineStyle: { color: 'rgba(30, 58, 95, 0.8)' } }
},
series: [{
name: '用户价值评估',
type: 'radar',
data: [
{
value: [85, 78, 92, 88, 75, 70],
name: '高价值用户',
itemStyle: { color: '#ffd700' },
areaStyle: { opacity: 0.3 }
},
{
value: [50, 65, 45, 55, 60, 40],
name: '普通用户',
itemStyle: { color: '#00d4ff' },
areaStyle: { opacity: 0.2 }
}
]
}]
};
userRadarChart.setOption(userRadarOption);
// 产品性能对比雷达图
var productRadarChart = echarts.init(document.getElementById('productRadar'));
var productRadarOption = {
tooltip: {},
radar: {
indicator: [
{ name: '性能', max: 100 },
{ name: '续航', max: 100 },
{ name: '屏幕', max: 100 },
{ name: '拍照', max: 100 },
{ name: '价格', max: 100 },
{ name: '外观', max: 100 }
],
center: ['50%', '55%'],
radius: '65%',
axisName: { color: '#888' },
splitArea: { areaStyle: { color: ['rgba(30, 58, 95, 0.3)', 'rgba(30, 58, 95, 0.5)'] } },
axisLine: { lineStyle: { color: 'rgba(30, 58, 95, 0.8)' } },
splitLine: { lineStyle: { color: 'rgba(30, 58, 95, 0.8)' } }
},
series: [{
name: '产品性能对比',
type: 'radar',
data: [
{
value: [90, 85, 88, 82, 75, 90],
name: '旗舰版',
itemStyle: { color: '#5470c6' },
areaStyle: { opacity: 0.3 }
},
{
value: [78, 88, 80, 75, 90, 82],
name: '标准版',
itemStyle: { color: '#91cc75' },
areaStyle: { opacity: 0.3 }
},
{
value: [85, 80, 92, 90, 65, 88],
name: '青春版',
itemStyle: { color: '#fac858' },
areaStyle: { opacity: 0.3 }
}
]
}]
};
productRadarChart.setOption(productRadarOption);
// 月度销售趋势图
var monthlyTrendChart = echarts.init(document.getElementById('monthlyTrend'));
var monthlyTrendOption = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } },
formatter: function(params) {
var res = params[0].name + ' 月度销售<br/>';
params.forEach(function(item) {
res += item.marker + item.seriesName + ':' + item.value + '万元<br/>';
});
return res;
}
},
legend: {
data: ['去年', '今年'],
textStyle: { color: '#888' },
top: '5%'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
axisLine: { lineStyle: { color: '#1e3a5f' } },
axisLabel: { color: '#888' }
},
yAxis: {
type: 'value',
name: '销售额(万元)',
axisLine: { lineStyle: { color: '#1e3a5f' } },
axisLabel: { color: '#888' },
splitLine: { lineStyle: { color: '#1e3a5f', type: 'dashed' } }
},
series: [
{
name: '去年',
type: 'line',
data: [820, 932, 901, 934, 1290, 1330, 1320, 1400, 1500, 1600, 1700, 1800],
smooth: true,
lineStyle: { color: '#666', type: 'dashed' },
symbol: 'emptycircle',
symbolSize: 6
},
{
name: '今年',
type: 'line',
data: [950, 1100, 1050, 1150, 1450, 1500, 1480, 1600, 1750, 1850, 1900, 2100],
smooth: true,
lineStyle: { color: '#00d4ff', width: 3 },
symbol: 'circle',
symbolSize: 8,
areaStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(0, 212, 255, 0.3)' },
{ offset: 1, color: 'rgba(0, 212, 255, 0)' }
]
}
}
}
]
};
monthlyTrendChart.setOption(monthlyTrendOption);
// 响应式处理
window.addEventListener('resize', function() {
salesTrendChart.resize();
categoryPieChart.resize();
regionBarChart.resize();
userRadarChart.resize();
productRadarChart.resize();
monthlyTrendChart.resize();
});
// 模拟数据更新
function updateData() {
// 更新核心指标
document.getElementById('totalSales').textContent =
'¥' + (Math.random() * 2000000 + 500000).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
document.getElementById('orderCount').textContent =
Math.floor(Math.random() * 10000 + 5000).toLocaleString();
document.getElementById('visitorCount').textContent =
Math.floor(Math.random() * 50000 + 20000).toLocaleString();
// 更新折线图数据
var newData = Array.from({length: 7}, () => Math.floor(Math.random() * 80 + 20));
salesTrendChart.setOption({
series: [{ data: newData }]
});
}
setInterval(updateData, 5000);
</script>
</body>
</html>
常见问题和解决方案
问题一:图表不显示或显示空白
症状:页面渲染了,但图表区域是空白的。
原因和解决:
// 常见原因1:容器没有设置宽高
// 错误做法
<div id="myChart"></div>
// 正确做法
<div id="myChart" style="width: 600px; height: 400px;"></div>
// 或者通过CSS设置
<style>
#myChart { width: 600px; height: 400px; }
</style>
// 常见原因2:在容器显示之前初始化
// 错误做法
<div id="myChart" style="display:none;"></div>
<script>
var chart = echarts.init(document.getElementById('myChart'));
// 此时容器隐藏,无法计算尺寸
</script>
// 正确做法:确保容器可见后再初始化
// 或者使用resize
setTimeout(function() {
chart.resize();
}, 100);
// 常见原因3:脚本加载顺序问题
// 确保 echarts 脚本在初始化代码之前加载
// 或者将初始化代码放在DOMContentLoaded事件中
document.addEventListener('DOMContentLoaded', function() {
var chart = echarts.init(document.getElementById('myChart'));
// ...
});
问题二:图表尺寸不正确或变形
症状:图表显示不完整、被截断或比例不对。
解决:
// 方案1:使用containLabel选项
var option = {
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true // 重要:确保标签不被截断
}
};
// 方案2:响应式resize
window.addEventListener('resize', function() {
chart.resize();
});
// 方案3:监听容器变化(适用于动态布局)
import ResizeObserver from 'resize-observer-polyfill';
const observer = new ResizeObserver(() => {
chart.resize();
});
observer.observe(chartDom);
问题三:数据更新后图表不刷新
症状:数据变了,但图表没有变化。
解决:
// 方案1:使用setOption,传入完整新配置
chart.setOption({
series: [{
data: newData // 替换整个数据
}]
});
// 方案2:使用setOption的notMerge参数(谨慎使用)
// notMerge: true 表示不合并,完全替换
chart.setOption({
series: [{
data: newData
}]
}, true); // 传入true表示不合并
// 方案3:数据量大时使用appendData
chart.setOption({
series: [{
type: 'line',
data: []
}]
});
// 逐条追加
chart.appendData({
seriesIndex: 0,
data: [120, 200, 150, 80]
});
问题四:移动端适配问题
症状:在手机上图表显示异常。
解决:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
// 方案1:根据屏幕宽度动态设置容器大小
function initChart() {
var width = window.innerWidth;
var height = Math.min(width * 0.6, 400); // 高度为宽度的60%,最大400px
chartDom.style.width = width + 'px';
chartDom.style.height = height + 'px';
var chart = echarts.init(chartDom);
chart.setOption(option);
}
// 方案2:使用rem单位
var option = {
grid: {
left: '8%',
right: '4%',
bottom: '12%'
}
};
// 方案3:响应式监听
window.addEventListener('resize', function() {
// 根据新宽度调整
var newWidth = window.innerWidth;
chartDom.style.width = newWidth + 'px';
chart.resize();
});
问题五:性能问题,数据量大时卡顿
症状:数据超过几千条时,图表渲染变慢或浏览器卡顿。
解决:
// 方案1:开启数据样本箱(大数据量时自动采样)
var option = {
series: [{
type: 'line',
sampling: 'lttb', // 'lttb', 'average', 'max', 'min', 'sum'
data: largeData
}]
};
// 方案2:分页加载或虚拟滚动
// 只渲染可视区域的数据
// 方案3:使用canvas替代svg
var option = {
series: [{
type: 'line',
progressive: 1000, // 渐进式渲染
progressiveThreshold: 5000 // 超过5000个点启用渐进渲染
}]
};
// 方案4:关闭部分功能提升性能
var option = {
series: [{
type: 'line',
label: { show: false }, // 关闭数据标签
lineStyle: { width: 1 }, // 减小线宽
symbol: 'none' // 关闭标记点
}]
};
问题六:自定义样式不生效
症状:配置了样式,但图表显示的是默认样式。
排查步骤:
// 1. 检查配置项是否正确拼写
// 错误:color
// 正确:itemStyle
// 2. 检查样式层级
var option = {
series: [{
itemStyle: {
color: '#ff0000' // 每个数据点的颜色
},
// 或者
itemStyle: {
color: function(params) {
return params.value > 100 ? '#ff0000' : '#00ff00';
}
}
}]
};
// 3. 检查是否被后续setOption覆盖
// 每次setOption都会合并配置,确保顺序正确
// 4. 使用echarts.getInstanceByDom检查实例
var chart = echarts.getInstanceByDom(document.getElementById('myChart'));
console.log(chart.getOption()); // 查看当前配置
进阶技巧
动态数据更新
// 模拟实时数据更新
var data = [0, 0, 0, 0, 0, 0, 0, 0];
var index = 0;
function updateChart() {
data.shift();
data.push(Math.floor(Math.random() * 100));
chart.setOption({
series: [{
data: data
}]
});
index++;
setTimeout(updateChart, 1000);
}
updateChart();
图表交互
var option = {
series: [{
type: 'line',
data: [120, 200, 150, 80, 70, 110, 130],
// 点击事件
click: function(params) {
alert('点击了:' + params.name + ',数值:' + params.value);
}
}],
// 鼠标悬停高亮
emphasis: {
focus: 'series'
}
};
// 绑定事件
chart.on('click', function(params) {
console.log(params);
});
chart.on('highlight', function(params) {
console.log('高亮', params);
});
chart.on('downplay', function(params) {
console.log('取消高亮', params);
});
多图联动
// 多个图表联动
var chart1 = echarts.init(document.getElementById('chart1'));
var chart2 = echarts.init(document.getElementById('chart2'));
// 设置联动
chart1.connect([chart2]);
chart2.connect([chart1]);
// 或者手动同步
chart1.on('dataZoom', function(params) {
chart2.dispatchAction({
type: 'dataZoom',
start: params.start,
end: params.end
});
});
最后的话
ECharts的学习曲线不算陡峭,但想要用好还是需要多练习。我建议的学习路径是:
- 先掌握柱状图、折线图、饼图这三种最常用的图表
- 然后学习雷达图、散点图、地图等特殊图表
- 最后深入研究主题定制、数据可视化设计等高级内容
记住,最好的学习方式就是动手做。把上面的代码复制下来,跑一遍,改一改,看看效果有什么不同。遇到问题就去官方文档查,或者到社区里提问。
图表不仅是数据的展示,更是故事的讲述。好的可视化能让复杂的数据变得直观易懂。希望这篇教程能帮你打开ECharts的大门,创造出更多有洞察力的数据可视化作品。
如果你在实战中遇到具体问题,随时可以来问。祝学习愉快!
