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. 数据加载:

    • 使用 fetch 或 axios 异步加载数据
    • 添加加载状态和错误处理
  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 的学习和使用中取得成功!