优化 ECharts 多条折线:折线数据不完整导致的X轴日期错乱问题

发布于:2025-07-10 ⋅ 阅读:(30) ⋅ 点赞:(0)

目录

一、简单介绍

1.1 常见类型

二、时间轴错乱问题

2.1 示例

2.2 示例完整代码

2.3 问题分析

2.4 修复方法

第一步

第二步

2.5 优化后完整代码


一、简单介绍

ECharts 是一款基于 JavaScript 的数据可视化图表库,动态图表是 ECharts 的一个重要应用场景,它可以让数据展示更加生动、直观,帮助用户更好地理解数据的变化趋势。以下是关于 ECharts 动态图表的详细介绍:

1.1 常见类型

  • 动态折线图:展示数据随时间或其他连续变量的变化趋势,比如股票价格走势、气温变化等。动态折线图可以通过定时更新数据,实时反映最新的变化情况。
  • 动态柱状图:用于对比不同类别数据的数值大小。动态效果可以是柱状图的增长、缩短,或者柱状图的顺序变换等,常用于展示数据的实时排名、销售业绩的动态对比等。
  • 动态饼图:突出显示各部分在总体中所占的比例。动态效果可以是扇区的大小变化、颜色闪烁等,适合用于展示市场份额的动态变化、不同类别数据占比的实时调整等。
  • 动态散点图:可以展示多个变量之间的关系,通过动态效果(如散点的移动、闪烁)来反映数据的实时变动,常用于分析数据的相关性、异常值的动态监测等。

二、时间轴错乱问题

2.1 示例

Examples - Apache ECharts参考:Examples - Apache ECharts

这是一幅多条折线组成的通过率趋势图,用于展示不同类别(1-1、1-2、1-3 )在多个时间节点(横轴日期)的通过率变化情况。

可以发现,页面出现了时间轴顺序错乱的效果,X 轴日期因字符串排序导致逻辑混乱,日期 0516 本应在 0627 之前,但图表中显示在最右侧,导致时间逻辑混乱。

2.2 示例完整代码

以下是一个完整的 HTML 示例,包含原始数据乱序、排序后再渲染图表的过程:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>折线图</title>
  <!-- 引入 ECharts CDN -->
  <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/dayjs/dist/dayjs.min.js"></script>
  <style>
    /* 给两个图表容器设置样式,避免重叠 */
    .chart-container {
      width: 800px; 
      height: 500px; 
      margin-bottom: 30px; /* 上下图表间距 */
    }
  </style>
</head>
<body>
  <!-- 图表容器 -->
  <div id="chart1" class="chart-container"></div>

  <script>
    // ===================== 数据及图表配置 =====================
    const rawData1 = [
        ['日期', '模块', '通过率'],
        // Phone - -1
        ['0418', '1-1', 98],
        ['0422', '1-1', 97],
        ['0425', '1-1', 96],
        ['0429', '1-1', 96],
        ['0513', '1-1', 99],
        ['0516', '1-1', '-'],
        ['0523', '1-1', 39],
        ['0527', '1-1', 96],
        ['0530', '1-1', 96],
        ['0603', '1-1', 98],
        ['0627', '1-1', ],
     
        ['0418', '1-2', 98],
        ['0422', '1-2', 98],
        ['0425', '1-2', 96],
        ['0429', '1-2', 98],
        ['0513', '1-2', 100],
        ['0516', '1-2', 97],
        ['0523', '1-2', 89],
        ['0527', '1-2', 98],
        ['0530', '1-2', 98],
        ['0603', '1-2', 100],
        ['0627', '1-2', 95],
      
        ['0418', '1-3', 89],
        ['0422', '1-3', 93],
        ['0425', '1-3', 89],
        ['0429', '1-3', 93],
        ['0513', '1-3', 97],
        ['0516', '1-3', '-'],
        ['0523', '1-3', 90],
        ['0527', '1-3', 95],
        ['0530', '1-3', 95],
        ['0603', '1-3', 91],
        ['0627', '1-3', 92],

        ['0418', '1-4', 95],
        ['0422', '1-4', 93],
        ['0425', '1-4', 96],
        ['0429', '1-4', 93],
        ['0513', '1-4', 97],
        ['0516', '1-4', '-'],
        ['0523', '1-4', 95],
        ['0527', '1-4', 93],
        ['0530', '1-4', 96],
        ['0603', '1-4', 94],
        ['0627', '1-4', '-'],
        ];


    // 数据预处理:过滤无效值
    function filterData(data){
      return data.filter((row, index) => {
        if (index === 0) return true;     // 跳过表头 & 通过率为'-'的项
        return row[2] !== '-'; 
      });
    }
    

    // ===================== 通用图表渲染函数 =====================
    /**
     * 渲染 ECharts 折线图
     * @param {string} domId - 容器 DOM ID
     * @param {array} data - 原始数据(格式:[['日期','维度','值'], ...])
     * @param {string} groupKey - 分组维度(如 '模块' 或 '机型_模块')
     * @param {string} titleText - 图表标题
     */
    function renderEChart(domId, data, groupKey, titleText) {
      const myChart = echarts.init(document.getElementById(domId));
      const groups = [...new Set(data.map(item => item[1]))]; // 提取分组(如模块、机型_模块)

      const datasetWithFilters = [];
      const seriesList = [];

      groups.forEach(group => {
        const datasetId = `dataset_${group}`;
        datasetWithFilters.push({
          id: datasetId,
          fromDatasetId: 'dataset_raw',
          transform: {
            type: 'filter',
            config: { dimension: groupKey, '=': group }
          }
        });

        seriesList.push({
          type: 'line',
          datasetId: datasetId,
          showSymbol: true,
          name: group,
          endLabel: {
            show: true,
            formatter: (params) => `${params.value[1]}`
          },
          emphasis: { focus: 'series' },
          encode: {
            x: '日期',
            y: '通过率',
            label: [groupKey, '通过率'],
            itemName: '日期',
            tooltip: ['通过率'],
            seriesName: '模块'
          }
        });
      });

      const option = {
        animationDuration: 2000,
        seriesLayoutBy: 'seriesName',
        dataset: [
          { id: 'dataset_raw', source: data },
          ...datasetWithFilters
        ],
        title: { text: titleText, left: 'center' },
        tooltip: {
          trigger: 'axis', 
          order: 'valueDesc',
          axisPointer: { type: 'shadow' } // 鼠标指向时显示阴影指示器,增强交互
        },
        xAxis: { 
          type: 'category',
          nameLocation: 'middle'
        },
        yAxis: {
          type: 'value',
          name: '通过率(%)',
          min: 10, // 过滤过低无效值(如测试阶段的异常值)
          max: 100, // 固定最大值为 100%,避免刻度溢出
          interval: 10 // 刻度间隔设为 10%,减少刻度密度
        },
        grid: {
          right: 150, // 从 100 调整为 150,增加右侧空间
          left: 60, // 左侧预留足够空间显示 Y 轴名称
          top: 50,
          bottom: 80 // 底部增加空间,避免 X 轴日期重叠
        },
        series: seriesList
      };

      myChart.setOption(option);
    }

    // ===================== 调用函数渲染图表 =====================
    renderEChart(
      'chart1', 
      filterData(rawData1),
      '模块', 
      '通过率趋势'
    );
  </script>
</body>
</html>

2.3 问题分析

多条折线的data中存在“-”,是因为我想要展示有的折线不存在该日期的数据,使用“-”来表示无数据情况。

从代码中可以看出 X 轴绘制是用的category 类型,是直接赋值渲染。category 类型的 X 轴,会严格按照 xAxis.data 数组的顺序展示类目。

发现直接赋值渲染,则页面会出现时间轴顺序错乱的效果。 

2.4 修复方法

第一步

重新修改折线图数据规则:需展示全量日期序列,无对应值的日期保留空数据占位。

例如,折线中的某一天数据为['0516', '1-3', '-'],时,其完整数据需包含所有日期节点,无值节点以空项填充,最终格式['0516', '1-3', ]。如下图所示,

修改后保存html文件,重新用浏览器打开。

经过第一步修改后的展示效果如下图所示。

当前展示效果中,X 轴的日期能按正常逻辑显示 ,但 1-1、1-3、1-4折线图都因存在无对应值的日期节点(数据中以空项形式呈现 ),在可视化呈现时出现了折线断裂(断层)的情况;而 1-2 折线图由于数据完整度较好,未出现此类问题,展示状态正常 。

第二步

参考:Documentation - Apache ECharts  配置文件

经过第一步修改后的展示效果中出现折线断裂情况。是因为,ECharts中的默认行为是若数据中存在 null(或空值占位 ),折线会在 null 处断裂,不连接两侧的有效点。

我们可以使用connectNulls这个属性来解决。

在 ECharts 中,connectNulls 是系列(series) 的配置属性,用于设置是否连接折线图中 null 数据点两侧的有效数据点。而connectNulls: true 是 ECharts 中折线图(line 系列) 的关键配置,用于控制包含 null 数据点时,折线是否连接两侧的有效数据,解决折线 “断层” 问题。

具体修改方法如下,

修改后保存html文件,重新用浏览器打开。

经过第二步修改后的展示效果如下图所示。

从展示效果来看, X 轴呈现的是从 0418 到 0627 的时间序列,此前因日期以字符串形式按字典序排序出现乱序状况。经修复后,如今日期已能依照正常的时间逻辑有序排列,保障了时间维度展示的准确性,为基于时间序列分析各系列通过率趋势筑牢基础,让数据随时间的变化呈现更贴合实际业务进程。

2.5 优化后完整代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>折线图</title>
  <!-- 引入 ECharts CDN -->
  <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/dayjs/dist/dayjs.min.js"></script>
  <style>
    /* 给两个图表容器设置样式,避免重叠 */
    .chart-container {
      width: 800px; 
      height: 500px; 
      margin-bottom: 30px; /* 上下图表间距 */
    }
  </style>
</head>
<body>
  <!-- 图表容器 -->
  <div id="chart1" class="chart-container"></div>

  <script>
    // ===================== 数据及图表配置 =====================
    const rawData1 = [
        ['日期', '模块', '通过率'],
        // Phone - -1
        ['0418', '1-1', 98],
        ['0422', '1-1', 97],
        ['0425', '1-1', 96],
        ['0429', '1-1', 96],
        ['0513', '1-1', 99],
        ['0516', '1-1', ],
        ['0523', '1-1', 39],
        ['0527', '1-1', 96],
        ['0530', '1-1', 96],
        ['0603', '1-1', 98],
        ['0627', '1-1', ],
     
        ['0418', '1-2', 98],
        ['0422', '1-2', 98],
        ['0425', '1-2', 96],
        ['0429', '1-2', 98],
        ['0513', '1-2', 100],
        ['0516', '1-2', 97],
        ['0523', '1-2', 89],
        ['0527', '1-2', 98],
        ['0530', '1-2', 98],
        ['0603', '1-2', 100],
        ['0627', '1-2', 95],
      
        ['0418', '1-3', 89],
        ['0422', '1-3', 93],
        ['0425', '1-3', 89],
        ['0429', '1-3', 93],
        ['0513', '1-3', 97],
        ['0516', '1-3', ],
        ['0523', '1-3', 90],
        ['0527', '1-3', 95],
        ['0530', '1-3', 95],
        ['0603', '1-3', 91],
        ['0627', '1-3', 92],

        ['0418', '1-4', 95],
        ['0422', '1-4', 93],
        ['0425', '1-4', 96],
        ['0429', '1-4', 93],
        ['0513', '1-4', 97],
        ['0516', '1-4', ],
        ['0523', '1-4', 95],
        ['0527', '1-4', 93],
        ['0530', '1-4', 96],
        ['0603', '1-4', 94],
        ['0627', '1-4', ],
        ];


    // 数据预处理:过滤无效值
    function filterData(data){
      return data.filter((row, index) => {
        if (index === 0) return true;     // 跳过表头 & 通过率为'-'的项
        return row[2] !== '-'; 
      });
    }
    

    // ===================== 通用图表渲染函数 =====================
    /**
     * 渲染 ECharts 折线图
     * @param {string} domId - 容器 DOM ID
     * @param {array} data - 原始数据(格式:[['日期','维度','值'], ...])
     * @param {string} groupKey - 分组维度(如 '模块' 或 '机型_模块')
     * @param {string} titleText - 图表标题
     */
    function renderEChart(domId, data, groupKey, titleText) {
      const myChart = echarts.init(document.getElementById(domId));
      const groups = [...new Set(data.map(item => item[1]))]; // 提取分组(如模块、机型_模块)

      const datasetWithFilters = [];
      const seriesList = [];

      groups.forEach(group => {
        const datasetId = `dataset_${group}`;
        datasetWithFilters.push({
          id: datasetId,
          fromDatasetId: 'dataset_raw',
          transform: {
            type: 'filter',
            config: { dimension: groupKey, '=': group }
          }
        });

        seriesList.push({
          type: 'line',
          connectNulls: true,
          datasetId: datasetId,
          showSymbol: true,
          name: group,
          endLabel: {
            show: true,
            formatter: (params) => `${params.value[1]}`
          },
          emphasis: { focus: 'series' },
          encode: {
            x: '日期',
            y: '通过率',
            label: [groupKey, '通过率'],
            itemName: '日期',
            tooltip: ['通过率'],
            seriesName: '模块'
          }
        });
      });

      const option = {
        animationDuration: 2000,
        seriesLayoutBy: 'seriesName',
        dataset: [
          { id: 'dataset_raw', source: data },
          ...datasetWithFilters
        ],
        title: { text: titleText, left: 'center' },
        tooltip: {
          trigger: 'axis', 
          order: 'valueDesc',
          axisPointer: { type: 'shadow' } // 鼠标指向时显示阴影指示器,增强交互
        },
        xAxis: { 
          type: 'category',
          nameLocation: 'middle'
        },
        yAxis: {
          type: 'value',
          name: '通过率(%)',
          min: 10, // 过滤过低无效值(如测试阶段的异常值)
          max: 100, // 固定最大值为 100%,避免刻度溢出
          interval: 10 // 刻度间隔设为 10%,减少刻度密度
        },
        grid: {
          right: 150, // 从 100 调整为 150,增加右侧空间
          left: 60, // 左侧预留足够空间显示 Y 轴名称
          top: 50,
          bottom: 80 // 底部增加空间,避免 X 轴日期重叠
        },
        series: seriesList
      };

      myChart.setOption(option);
    }

    // ===================== 调用函数渲染图表 =====================
    renderEChart(
      'chart1', 
      filterData(rawData1),
      '模块', 
      '通过率趋势'
    );
  </script>
</body>
</html>

通过以上步骤,就能解决 category 类型 X 轴显示乱序的问题,让 X 轴按照你期望的固定顺序展示类目啦。


网站公告

今日签到

点亮在社区的每一天
去签到