1. ECharts 简介

ECharts 是一个由百度开源的数据可视化库,现由 Apache 基金会维护。它是一个使用 JavaScript 实现的开源可视化库,可以流畅地运行在 PC 和移动设备上,兼容当前绝大部分浏览器。ECharts 提供了直观、生动、可交互、可高度个性化定制的数据可视化图表。

1.1 为什么选择 ECharts?

  • 丰富的图表类型:支持折线图、柱状图、饼图、散点图、地图、雷达图、K线图、热力图、树图、旭日图等多种图表。
  • 强大的交互能力:支持数据区域缩放、图例切换、数据视图、值域漫游等交互操作。
  • 高度可定制:可以自定义主题、颜色、字体、动画等,满足个性化需求。
  • 跨平台兼容:支持 Canvas 和 SVG 渲染,兼容主流浏览器。
  • 社区活跃:拥有庞大的用户群体和丰富的文档资源。

1.2 ECharts 与其他可视化库的对比

特性 ECharts D3.js Chart.js Highcharts
学习曲线 中等 陡峭 简单 中等
图表类型 非常丰富 非常灵活 基础 丰富
交互能力 强大 极强 基础 强大
定制程度 极高 中等
商业使用 免费 免费 免费 需授权
文档质量 优秀 详细 良好 优秀

2. 环境准备与基础概念

2.1 引入 ECharts

2.1.1 通过 CDN 引入(推荐初学者)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts 入门示例</title>
    <!-- 引入 ECharts 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
</head>
<body>
    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
    <div id="main" style="width: 600px;height:400px;"></div>
    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));
        
        // 指定图表的配置项和数据
        var option = {
            title: {
                text: 'ECharts 入门示例'
            },
            tooltip: {},
            legend: {
                data:['销量']
            },
            xAxis: {
                data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
            },
            yAxis: {},
            series: [{
                name: '销量',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };
        
        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
    </script>
</body>
</html>

2.1.2 通过 npm 安装(推荐项目开发)

# 安装 ECharts
npm install echarts --save

# 在 Vue/React 项目中使用
# Vue 示例
import * as echarts from 'echarts';

export default {
  mounted() {
    const chartDom = document.getElementById('main');
    const myChart = echarts.init(chartDom);
    // 配置项...
  }
}

2.2 核心概念

2.2.1 实例(Instance)

ECharts 的每个图表都是一个实例。通过 echarts.init() 创建实例。

// 创建实例
const chart = echarts.init(document.getElementById('chart-container'));

// 销毁实例(重要!防止内存泄漏)
chart.dispose();

2.2.2 配置项(Option)

配置项是 ECharts 的核心,它是一个 JavaScript 对象,定义了图表的所有属性。

const option = {
    // 标题
    title: {
        text: '主标题',
        subtext: '副标题'
    },
    // 提示框
    tooltip: {
        trigger: 'axis'
    },
    // 图例
    legend: {
        data: ['系列1', '系列2']
    },
    // X轴
    xAxis: {
        type: 'category',
        data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
    },
    // Y轴
    yAxis: {
        type: 'value'
    },
    // 数据系列
    series: [
        {
            name: '系列1',
            type: 'line',
            data: [820, 932, 901, 934, 1290, 1330, 1320]
        }
    ]
};

2.2.3 数据系列(Series)

数据系列是图表的核心数据部分,可以包含多个系列,每个系列可以是不同的图表类型。

// 多系列示例
series: [
    {
        name: '降水量',
        type: 'bar',
        data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6]
    },
    {
        name: '蒸发量',
        type: 'line',
        yAxisIndex: 1, // 使用第二个Y轴
        data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3]
    }
]

3. 基础图表实战

3.1 柱状图(Bar Chart)

柱状图用于比较不同类别的数据。

// 柱状图配置
const barOption = {
    title: {
        text: '2023年各季度销售额',
        left: 'center'
    },
    tooltip: {
        trigger: 'axis',
        axisPointer: {
            type: 'shadow'
        }
    },
    legend: {
        data: ['产品A', '产品B', '产品C'],
        top: 30
    },
    grid: {
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true
    },
    xAxis: {
        type: 'category',
        data: ['Q1', 'Q2', 'Q3', 'Q4']
    },
    yAxis: {
        type: 'value',
        name: '销售额(万元)'
    },
    series: [
        {
            name: '产品A',
            type: 'bar',
            stack: 'total', // 堆叠柱状图
            emphasis: {
                focus: 'series'
            },
            data: [320, 302, 301, 334]
        },
        {
            name: '产品B',
            type: 'bar',
            stack: 'total',
            data: [220, 182, 191, 234]
        },
        {
            name: '产品C',
            type: 'bar',
            stack: 'total',
            data: [150, 212, 201, 154]
        }
    ]
};

3.2 折线图(Line Chart)

折线图用于展示数据随时间或类别的变化趋势。

// 折线图配置
const lineOption = {
    title: {
        text: '2023年月度用户增长趋势',
        left: 'center'
    },
    tooltip: {
        trigger: 'axis'
    },
    legend: {
        data: ['新增用户', '活跃用户'],
        top: 30
    },
    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',
            smooth: true, // 平滑曲线
            data: [120, 132, 101, 134, 90, 230, 210, 220, 182, 191, 234, 290],
            markPoint: {
                data: [
                    { type: 'max', name: '最大值' },
                    { type: 'min', name: '最小值' }
                ]
            },
            markLine: {
                data: [
                    { type: 'average', name: '平均值' }
                ]
            }
        },
        {
            name: '活跃用户',
            type: 'line',
            smooth: true,
            data: [82, 93, 90, 93, 129, 133, 132, 140, 150, 160, 170, 180]
        }
    ]
};

3.3 饼图(Pie Chart)

饼图用于显示部分与整体的关系。

// 饼图配置
const pieOption = {
    title: {
        text: '2023年市场份额分布',
        left: 'center'
    },
    tooltip: {
        trigger: 'item',
        formatter: '{a} <br/>{b}: {c} ({d}%)'
    },
    legend: {
        orient: 'vertical',
        left: 'left',
        data: ['产品A', '产品B', '产品C', '产品D', '产品E']
    },
    series: [
        {
            name: '市场份额',
            type: 'pie',
            radius: '50%',
            data: [
                { value: 1048, name: '产品A' },
                { value: 735, name: '产品B' },
                { value: 580, name: '产品C' },
                { value: 484, name: '产品D' },
                { value: 300, name: '产品E' }
            ],
            emphasis: {
                itemStyle: {
                    shadowBlur: 10,
                    shadowOffsetX: 0,
                    shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
            },
            label: {
                show: true,
                formatter: '{b}: {c} ({d}%)'
            }
        }
    ]
};

3.4 散点图(Scatter Chart)

散点图用于展示两个变量之间的关系。

// 散点图配置
const scatterOption = {
    title: {
        text: '广告投入与销售额关系',
        left: 'center'
    },
    tooltip: {
        trigger: 'axis',
        axisPointer: {
            type: 'cross'
        }
    },
    legend: {
        data: ['产品A', '产品B'],
        top: 30
    },
    grid: {
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true
    },
    xAxis: {
        type: 'value',
        name: '广告投入(万元)',
        splitLine: {
            show: false
        }
    },
    yAxis: {
        type: 'value',
        name: '销售额(万元)'
    },
    series: [
        {
            name: '产品A',
            type: 'scatter',
            symbolSize: 10,
            data: [
                [10, 8.04],
                [8, 6.95],
                [13, 7.58],
                [9, 8.81],
                [11, 8.33],
                [14, 9.96],
                [6, 7.24],
                [4, 4.26],
                [12, 10.84],
                [7, 4.82],
                [5, 5.68]
            ],
            itemStyle: {
                color: '#5470c6'
            }
        },
        {
            name: '产品B',
            type: 'scatter',
            symbolSize: 10,
            data: [
                [10, 9.14],
                [8, 8.14],
                [13, 8.74],
                [9, 8.77],
                [11, 9.26],
                [14, 8.10],
                [6, 6.13],
                [4, 3.10],
                [12, 9.13],
                [7, 7.26],
                [5, 4.74]
            ],
            itemStyle: {
                color: '#91cc75'
            }
        }
    ]
};

4. 高级图表与交互

4.1 地图可视化

ECharts 支持多种地图,包括世界地图、中国地图、省份地图等。

// 中国地图配置
const chinaMapOption = {
    title: {
        text: '2023年各省份销售额',
        left: 'center'
    },
    tooltip: {
        trigger: 'item',
        formatter: '{b}<br/>销售额:{c} 万元'
    },
    visualMap: {
        min: 0,
        max: 1000,
        text: ['高', '低'],
        realtime: false,
        calculable: true,
        inRange: {
            color: ['#50a3ba', '#eac736', '#d94e5d']
        }
    },
    series: [
        {
            name: '销售额',
            type: 'map',
            map: 'china',
            roam: true, // 允许拖拽和缩放
            label: {
                show: true,
                fontSize: 10
            },
            emphasis: {
                label: {
                    show: true
                }
            },
            data: [
                { name: '北京', value: 800 },
                { name: '天津', value: 300 },
                { name: '上海', value: 900 },
                { name: '重庆', value: 400 },
                { name: '河北', value: 500 },
                { name: '河南', value: 600 },
                { name: '云南', value: 200 },
                { name: '辽宁', value: 350 },
                { name: '黑龙江', value: 250 },
                { name: '湖南', value: 450 },
                { name: '安徽', value: 380 },
                { name: '山东', value: 700 },
                { name: '新疆', value: 150 },
                { name: '江苏', value: 850 },
                { name: '浙江', value: 820 },
                { name: '江西', value: 320 },
                { name: '湖北', value: 550 },
                { name: '广西', value: 280 },
                { name: '甘肃', value: 180 },
                { name: '山西', value: 420 },
                { name: '内蒙古', value: 200 },
                { name: '陕西', value: 480 },
                { name: '吉林', value: 220 },
                { name: '福建', value: 650 },
                { name: '贵州', value: 160 },
                { name: '广东', value: 950 },
                { name: '青海', value: 100 },
                { name: '西藏', value: 80 },
                { name: '四川', value: 620 },
                { name: '宁夏', value: 120 },
                { name: '海南', value: 140 },
                { name: '台湾', value: 300 },
                { name: '香港', value: 400 },
                { name: '澳门', value: 150 }
            ]
        }
    ]
};

// 注意:需要先注册地图数据
// 从 https://echarts.apache.org/zh/download-map.html 下载地图JSON文件
// 然后注册:
// fetch('china.json').then(response => response.json()).then(geoJson => {
//     echarts.registerMap('china', geoJson);
//     // 然后初始化图表
// });

4.2 动态数据更新

ECharts 支持动态更新数据,非常适合实时数据展示。

// 动态数据更新示例
let chart = echarts.init(document.getElementById('dynamic-chart'));
let data = [];
let now = new Date();
let value = Math.random() * 1000;

// 初始化数据
for (let i = 0; i < 100; i++) {
    data.push({
        name: now.toString(),
        value: [
            [now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'),
            Math.round(value)
        ]
    });
    now = new Date(now - 86400000); // 一天前
    value = value + Math.random() * 20 - 10;
}

const dynamicOption = {
    title: {
        text: '实时数据监控',
        left: 'center'
    },
    tooltip: {
        trigger: 'axis',
        formatter: function (params) {
            params = params[0];
            var date = new Date(params.name);
            return (
                date.getDate() +
                '/' +
                (date.getMonth() + 1) +
                '/' +
                date.getFullYear() +
                ' : ' +
                params.value[1]
            );
        },
        axisPointer: {
            animation: false
        }
    },
    xAxis: {
        type: 'time',
        splitLine: {
            show: false
        }
    },
    yAxis: {
        type: 'value',
        boundaryGap: [0, '100%'],
        splitLine: {
            show: false
        }
    },
    series: [
        {
            name: '模拟数据',
            type: 'line',
            showSymbol: false,
            hoverAnimation: false,
            data: data
        }
    ]
};

chart.setOption(dynamicOption);

// 模拟实时数据更新
setInterval(function () {
    now = new Date(+now + 86400000);
    value = value + Math.random() * 20 - 10;
    
    data.shift();
    data.push({
        name: now.toString(),
        value: [
            [now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'),
            Math.round(value)
        ]
    });
    
    chart.setOption({
        series: [{
            data: data
        }]
    });
}, 2000);

4.3 交互功能

ECharts 提供了丰富的交互功能,如数据缩放、图例切换、数据视图等。

// 交互功能配置
const interactiveOption = {
    title: {
        text: '交互功能演示',
        left: 'center'
    },
    tooltip: {
        trigger: 'axis',
        axisPointer: {
            type: 'cross'
        }
    },
    legend: {
        data: ['系列1', '系列2', '系列3'],
        top: 30
    },
    grid: {
        left: '3%',
        right: '4%',
        bottom: '10%',
        containLabel: true
    },
    dataZoom: [
        {
            type: 'slider', // 滑块型数据区域缩放组件
            start: 0,
            end: 100
        },
        {
            type: 'inside', // 内置型数据区域缩放组件
            start: 0,
            end: 100
        }
    ],
    toolbox: {
        feature: {
            dataZoom: {
                yAxisIndex: 'none'
            },
            restore: {}, // 还原
            saveAsImage: {}, // 保存为图片
            dataView: {}, // 数据视图
            magicType: {
                type: ['line', 'bar', 'stack', 'tiled'] // 图表类型切换
            }
        }
    },
    xAxis: {
        type: 'category',
        data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
    },
    yAxis: {
        type: 'value'
    },
    series: [
        {
            name: '系列1',
            type: 'line',
            data: [820, 932, 901, 934, 1290, 1330, 1320]
        },
        {
            name: '系列2',
            type: 'line',
            data: [620, 732, 701, 734, 1090, 1130, 1120]
        },
        {
            name: '系列3',
            type: 'line',
            data: [520, 632, 601, 634, 990, 1030, 1020]
        }
    ]
};

5. 实战项目:销售数据仪表盘

5.1 项目概述

我们将创建一个销售数据仪表盘,包含以下组件:

  • 总销售额统计
  • 月度销售趋势图
  • 产品类别占比饼图
  • 区域销售地图
  • 实时数据更新

5.2 HTML 结构

<!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.4.3/dist/echarts.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background-color: #f5f5f5;
            font-family: 'Microsoft YaHei', sans-serif;
        }
        .dashboard {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            grid-template-rows: repeat(3, 300px);
            gap: 15px;
        }
        .card {
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 12px rgba(0,0,0,0.1);
            padding: 15px;
            display: flex;
            flex-direction: column;
        }
        .card-title {
            font-size: 16px;
            font-weight: bold;
            margin-bottom: 10px;
            color: #333;
        }
        .chart-container {
            flex: 1;
            min-height: 0;
        }
        .stat-card {
            grid-column: span 1;
            grid-row: span 1;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            justify-content: center;
            align-items: center;
        }
        .stat-value {
            font-size: 32px;
            font-weight: bold;
            margin: 10px 0;
        }
        .stat-label {
            font-size: 14px;
            opacity: 0.9;
        }
        .chart-large {
            grid-column: span 2;
            grid-row: span 2;
        }
        .chart-medium {
            grid-column: span 2;
            grid-row: span 1;
        }
        .chart-small {
            grid-column: span 1;
            grid-row: span 1;
        }
    </style>
</head>
<body>
    <div class="dashboard">
        <!-- 统计卡片 -->
        <div class="card stat-card">
            <div class="stat-label">总销售额</div>
            <div class="stat-value">¥2,456,789</div>
            <div class="stat-label">同比增长 12.5%</div>
        </div>
        <div class="card stat-card" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
            <div class="stat-label">订单数量</div>
            <div class="stat-value">12,847</div>
            <div class="stat-label">环比增长 8.2%</div>
        </div>
        <div class="card stat-card" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
            <div class="stat-label">平均客单价</div>
            <div class="stat-value">¥191.2</div>
            <div class="stat-label">同比增长 5.3%</div>
        </div>
        <div class="card stat-card" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
            <div class="stat-label">活跃用户</div>
            <div class="stat-value">8,542</div>
            <div class="stat-label">环比增长 15.7%</div>
        </div>
        
        <!-- 图表区域 -->
        <div class="card chart-large">
            <div class="card-title">月度销售趋势</div>
            <div id="trendChart" class="chart-container"></div>
        </div>
        
        <div class="card chart-medium">
            <div class="card-title">产品类别占比</div>
            <div id="pieChart" class="chart-container"></div>
        </div>
        
        <div class="card chart-medium">
            <div class="card-title">区域销售分布</div>
            <div id="mapChart" class="chart-container"></div>
        </div>
        
        <div class="card chart-small">
            <div class="card-title">实时数据监控</div>
            <div id="realtimeChart" class="chart-container"></div>
        </div>
    </div>
    
    <script>
        // 初始化所有图表
        function initCharts() {
            // 1. 月度销售趋势图
            const trendChart = echarts.init(document.getElementById('trendChart'));
            const trendOption = {
                tooltip: {
                    trigger: 'axis',
                    axisPointer: {
                        type: 'cross'
                    }
                },
                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',
                        name: '销售额(万元)'
                    },
                    {
                        type: 'value',
                        name: '订单量'
                    }
                ],
                series: [
                    {
                        name: '销售额',
                        type: 'line',
                        yAxisIndex: 0,
                        data: [120, 132, 101, 134, 90, 230, 210, 220, 182, 191, 234, 290],
                        smooth: true,
                        itemStyle: {
                            color: '#5470c6'
                        }
                    },
                    {
                        name: '订单量',
                        type: 'bar',
                        yAxisIndex: 1,
                        data: [800, 900, 700, 950, 600, 1500, 1400, 1450, 1200, 1300, 1500, 1800],
                        itemStyle: {
                            color: '#91cc75'
                        }
                    }
                ]
            };
            trendChart.setOption(trendOption);
            
            // 2. 产品类别占比饼图
            const pieChart = echarts.init(document.getElementById('pieChart'));
            const pieOption = {
                tooltip: {
                    trigger: 'item',
                    formatter: '{a} <br/>{b}: {c} ({d}%)'
                },
                legend: {
                    orient: 'vertical',
                    left: 'left',
                    data: ['电子产品', '服装', '食品', '家居', '其他']
                },
                series: [
                    {
                        name: '销售额占比',
                        type: 'pie',
                        radius: ['40%', '70%'],
                        avoidLabelOverlap: false,
                        itemStyle: {
                            borderRadius: 10,
                            borderColor: '#fff',
                            borderWidth: 2
                        },
                        label: {
                            show: false,
                            position: 'center'
                        },
                        emphasis: {
                            label: {
                                show: true,
                                fontSize: 16,
                                fontWeight: 'bold'
                            }
                        },
                        labelLine: {
                            show: false
                        },
                        data: [
                            { value: 800, name: '电子产品' },
                            { value: 600, name: '服装' },
                            { value: 400, name: '食品' },
                            { value: 300, name: '家居' },
                            { value: 200, name: '其他' }
                        ]
                    }
                ]
            };
            pieChart.setOption(pieOption);
            
            // 3. 区域销售地图
            const mapChart = echarts.init(document.getElementById('mapChart'));
            const mapOption = {
                tooltip: {
                    trigger: 'item',
                    formatter: '{b}<br/>销售额:{c} 万元'
                },
                visualMap: {
                    min: 0,
                    max: 1000,
                    text: ['高', '低'],
                    realtime: false,
                    calculable: true,
                    inRange: {
                        color: ['#50a3ba', '#eac736', '#d94e5d']
                    },
                    bottom: 10,
                    left: 10
                },
                series: [
                    {
                        name: '销售额',
                        type: 'map',
                        map: 'china',
                        roam: true,
                        label: {
                            show: true,
                            fontSize: 10
                        },
                        emphasis: {
                            label: {
                                show: true
                            }
                        },
                        data: [
                            { name: '北京', value: 800 },
                            { name: '上海', value: 900 },
                            { name: '广东', value: 950 },
                            { name: '江苏', value: 850 },
                            { name: '浙江', value: 820 },
                            { name: '山东', value: 700 },
                            { name: '四川', value: 620 },
                            { name: '湖北', value: 550 },
                            { name: '河南', value: 600 },
                            { name: '河北', value: 500 }
                        ]
                    }
                ]
            };
            mapChart.setOption(mapOption);
            
            // 4. 实时数据监控
            const realtimeChart = echarts.init(document.getElementById('realtimeChart'));
            let realtimeData = [];
            let now = new Date();
            let value = Math.random() * 1000;
            
            for (let i = 0; i < 20; i++) {
                realtimeData.push({
                    name: now.toString(),
                    value: [
                        [now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'),
                        Math.round(value)
                    ]
                });
                now = new Date(now - 3600000); // 一小时前
                value = value + Math.random() * 20 - 10;
            }
            
            const realtimeOption = {
                tooltip: {
                    trigger: 'axis',
                    formatter: function (params) {
                        params = params[0];
                        var date = new Date(params.name);
                        return (
                            date.getHours() +
                            ':' +
                            (date.getMinutes() < 10 ? '0' : '') +
                            date.getMinutes() +
                            ' : ' +
                            params.value[1]
                        );
                    },
                    axisPointer: {
                        animation: false
                    }
                },
                xAxis: {
                    type: 'time',
                    splitLine: {
                        show: false
                    }
                },
                yAxis: {
                    type: 'value',
                    boundaryGap: [0, '100%'],
                    splitLine: {
                        show: false
                    }
                },
                series: [
                    {
                        name: '实时数据',
                        type: 'line',
                        showSymbol: false,
                        hoverAnimation: false,
                        data: realtimeData,
                        areaStyle: {
                            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                                { offset: 0, color: 'rgba(83, 168, 255, 0.5)' },
                                { offset: 1, color: 'rgba(83, 168, 255, 0.1)' }
                            ])
                        },
                        lineStyle: {
                            color: '#53a8ff'
                        }
                    }
                ]
            };
            realtimeChart.setOption(realtimeOption);
            
            // 模拟实时数据更新
            setInterval(function () {
                now = new Date(+now + 3600000);
                value = value + Math.random() * 20 - 10;
                
                realtimeData.shift();
                realtimeData.push({
                    name: now.toString(),
                    value: [
                        [now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'),
                        Math.round(value)
                    ]
                });
                
                realtimeChart.setOption({
                    series: [{
                        data: realtimeData
                    }]
                });
            }, 3000);
            
            // 响应式调整
            window.addEventListener('resize', function() {
                trendChart.resize();
                pieChart.resize();
                mapChart.resize();
                realtimeChart.resize();
            });
        }
        
        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', function() {
            // 注册地图数据(这里使用模拟数据,实际项目中需要加载真实地图JSON)
            // fetch('china.json').then(response => response.json()).then(geoJson => {
            //     echarts.registerMap('china', geoJson);
            //     initCharts();
            // });
            
            // 为了演示,我们直接初始化图表(地图将显示为空白)
            initCharts();
        });
    </script>
</body>
</html>

5.3 项目优化建议

  1. 性能优化

    • 使用 echarts.dispose() 销毁不再需要的图表实例
    • 对于大数据量,考虑使用 large: true 选项
    • 使用 animation: false 关闭动画以提升性能
  2. 响应式设计

    • 使用 window.addEventListener('resize', function() { chart.resize(); })
    • 考虑使用 ResizeObserver API 进行更精确的响应式控制
  3. 数据加载

    • 使用 fetchaxios 异步加载数据
    • 添加加载状态和错误处理
  4. 主题定制

    • 使用 ECharts 内置主题(dark, light)
    • 自定义主题:echarts.registerTheme('custom', customTheme)

6. 常见问题与解决方案

6.1 图表不显示

问题:图表区域空白,没有显示任何内容。

解决方案

  1. 检查 DOM 元素是否存在且具有宽高
  2. 确保 ECharts 库已正确加载
  3. 检查 setOption 是否被调用
  4. 查看浏览器控制台是否有错误信息
// 调试代码
console.log('DOM元素:', document.getElementById('main'));
console.log('ECharts实例:', myChart);
console.log('配置项:', option);

6.2 数据更新不生效

问题:调用 setOption 后图表没有变化。

解决方案

  1. 确保使用了正确的配置项格式
  2. 对于动态数据,使用 merge: true 参数
  3. 检查数据格式是否正确
// 正确的数据更新方式
myChart.setOption({
    series: [{
        data: newData
    }]
}, true); // 第二个参数为 true 表示合并配置

6.3 内存泄漏

问题:页面刷新或切换时,图表实例没有被销毁。

解决方案

  1. 在组件卸载时销毁实例
  2. 使用 echarts.dispose() 方法
// Vue 组件示例
beforeDestroy() {
    if (this.chart) {
        this.chart.dispose();
        this.chart = null;
    }
}

6.4 移动端适配

问题:在移动设备上显示不正常。

解决方案

  1. 设置正确的 viewport
  2. 使用百分比或 rem 单位
  3. 调整图表大小和字体
// 移动端适配示例
function resizeChart() {
    const chartDom = document.getElementById('chart');
    const width = window.innerWidth;
    const height = window.innerHeight;
    
    // 根据屏幕尺寸调整配置
    const option = {
        grid: {
            left: width < 768 ? '5%' : '10%',
            right: width < 768 ? '5%' : '10%',
            bottom: width < 768 ? '10%' : '15%'
        },
        xAxis: {
            axisLabel: {
                fontSize: width < 768 ? 10 : 12
            }
        },
        yAxis: {
            axisLabel: {
                fontSize: width < 768 ? 10 : 12
            }
        }
    };
    
    chart.setOption(option);
    chart.resize();
}

7. 进阶学习路径

7.1 官方文档与资源

7.2 推荐学习资源

  1. 书籍

    • 《ECharts 数据可视化》
    • 《深入浅出 ECharts》
  2. 在线课程

    • 慕课网 ECharts 课程
    • 极客时间数据可视化专栏
  3. 开源项目

    • ECharts 官方示例
    • Vue-ECharts 组件库
    • React-ECharts 组件库

7.3 实践项目建议

  1. 个人项目

    • 个人博客访问统计
    • 运动数据可视化
    • 股票数据监控
  2. 企业项目

    • 业务数据仪表盘
    • 实时监控系统
    • 数据分析报告
  3. 开源贡献

    • 为 ECharts 贡献示例
    • 开发 ECharts 插件
    • 修复 ECharts Bug

8. 总结

ECharts 是一个功能强大、易于上手的数据可视化库。通过本指南,您应该已经掌握了:

  1. 基础概念:实例、配置项、数据系列
  2. 基础图表:柱状图、折线图、饼图、散点图
  3. 高级功能:地图、动态数据、交互功能
  4. 实战项目:销售数据仪表盘的完整实现
  5. 问题解决:常见问题的排查与优化

8.1 关键要点回顾

  • 配置项是核心:所有图表行为都通过 option 对象控制
  • 数据格式很重要:确保数据格式与图表类型匹配
  • 响应式设计:图表需要适应不同屏幕尺寸
  • 性能优化:大数据量时需要特殊处理

8.2 下一步行动

  1. 动手实践:尝试修改示例代码,创建自己的图表
  2. 深入学习:阅读官方文档,探索更多图表类型
  3. 项目实战:将 ECharts 集成到实际项目中
  4. 社区参与:加入 ECharts 社区,分享经验

8.3 最后建议

数据可视化不仅仅是技术实现,更是数据故事的讲述。在使用 ECharts 时,请记住:

  • 明确目标:你想通过图表传达什么信息?
  • 选择合适的图表:不同的数据适合不同的图表类型
  • 保持简洁:避免过度装饰,让数据自己说话
  • 注重交互:良好的交互能提升用户体验

祝您在 ECharts 的学习和使用中取得成功!