You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ECharts散点图中独立分组展示两组标签

在Angular中实现ECharts分组标签独立避重的散点图

问题描述

在Angular项目中使用ECharts创建包含大量数据的交互式散点图,需在同一张图内展示两组数据点:一组标签固定在图表顶部区域,另一组固定在数据点下方区域。使用labelLayout的moveOverlap: 'shiftX'配置解决标签重叠时,发现该配置会全局检测X轴方向的所有标签重叠,导致上下区域的标签互相干扰,出现不合理的水平偏移,显示效果怪异且性能低下。尝试过拆分网格、坐标轴、系列等方式,仍无法实现对两组标签分别应用shiftX避重规则。

可行解决方案

核心思路是利用ECharts的labelLayout自定义能力,手动划分标签分组区域,仅对同一区域内的标签进行重叠检测和偏移处理,不同区域标签互不干扰。

步骤1:标记标签分组

通过系列索引(seriesIndex)为两组标签划分独立分组,分别存储每组标签的位置和宽度信息。

步骤2:实现分组内的自定义避重逻辑

放弃全局moveOverlap配置,在labelLayout函数中手动计算同组标签的位置,仅调整同组内重叠标签的X坐标:

  • 收集同组内所有标签的初始位置与宽度
  • 按X轴坐标排序后,依次调整标签位置,避免与同组前一个标签重叠
  • 返回调整后的最终位置

修改后的完整代码

const data = [
  [[28604,77,17096869,'Australia',1990],[31163,77.4,27662440,'Canada',1990],[1516,68,1154605773,'China',1990],[13670,74.7,10582082,'Cuba',1990],[28599,75,4986705,'Finland',1990],[29476,77.1,56943299,'France',1990],[31476,75.4,78958237,'Germany',1990],[28666,78.1,254830,'Iceland',1990],[1777,57.7,870601776,'India',1990],[29550,79.1,122249285,'Japan',1990],[2076,67.9,20194354,'North Korea',1990],[12087,72,42972254,'South Korea',1990],[24021,75.4,3397534,'New Zealand',1990],[43296,76.8,4240375,'Norway',1990],[10088,70.8,38195258,'Poland',1990],[19349,69.6,147568552,'Russia',1990],[10670,67.3,53994605,'Turkey',1990],[26424,75.7,57110117,'United Kingdom',1990],[37062,75.4,252847810,'United States',1990]],
  [[44056,81.8,23968973,'Australia',2015],[43294,81.7,35939927,'Canada',2015],[13334,76.9,1376048943,'China',2015],[21291,78.5,11389562,'Cuba',2015],[38923,80.8,5503457,'Finland',2015],[37599,81.9,64395345,'France',2015],[44053,81.1,80688545,'Germany',2015],[42182,82.8,329425,'Iceland',2015],[5903,66.8,1311050527,'India',2015],[36162,83.5,126573481,'Japan',2015],[1390,71.4,25155317,'North Korea',2015],[34644,80.7,50293439,'South Korea',2015],[34186,80.6,4528526,'New Zealand',2015],[64304,81.6,5210967,'Norway',2015],[24787,77.3,38611794,'Poland',2015],[23038,73.13,143456918,'Russia',2015],[19360,76.5,78665830,'Turkey',2015],[38225,81.4,64715810,'United Kingdom',2015],[53354,79.1,321773631,'United States',2015]]
];

// 存储两组标签的位置与宽度,实现分组隔离
const groupLabelInfo = {
  0: [], // 顶部区域标签组
  1: []  // 底部区域标签组
};

option = {
  xAxis: [
    { splitLine: { show: false } },
    { splitLine: { show: false } }
  ],
  yAxis: [
    { splitLine: { show: false }, scale: true },
    { splitLine: { show: false }, scale: true }
  ],
  grid: { left: 40, right: 130 },
  series: [
    {
      name: '1990',
      data: data[0],
      xAxisIndex: 0,
      yAxisIndex: 0,
      type: 'scatter',
      symbolSize: (data: any) => Math.sqrt(data[2]) / 5e2,
      emphasis: { focus: 'self' },
      labelLayout: (param: any) => {
        const groupIndex = 0;
        const labelWidth = param.labelRect.width;
        const targetY = param.chart.getHeight() - 750;
        let targetX = param.x;

        // 同组内标签避重
        const sameGroupLabels = groupLabelInfo[groupIndex];
        if (sameGroupLabels.length > 0) {
          const lastLabel = sameGroupLabels[sameGroupLabels.length - 1];
          const minGap = 5; // 标签间最小间距
          const overlapOffset = lastLabel.x + lastLabel.width + minGap - targetX;
          if (overlapOffset > 0) {
            targetX += overlapOffset;
          }
        }

        sameGroupLabels.push({ x: targetX, width: labelWidth });
        return { x: targetX, y: targetY };
      },
      labelLine: { show: true, lineStyle: { color: '#bbb' } },
      label: {
        show: true,
        formatter: (param: any) => param.data[3],
        position: 'right',
        minMargin: 2
      }
    },
    {
      name: '2015',
      data: data[1],
      xAxisIndex: 1,
      yAxisIndex: 1,
      type: 'scatter',
      symbolSize: (data: any) => Math.sqrt(data[2]) / 5e2,
      emphasis: { focus: 'self' },
      labelLayout: (param: any) => {
        const groupIndex = 1;
        const labelWidth = param.labelRect.width;
        const targetY = param.chart.getHeight() - 30;
        let targetX = param.x;

        // 同组内标签避重
        const sameGroupLabels = groupLabelInfo[groupIndex];
        if (sameGroupLabels.length > 0) {
          const lastLabel = sameGroupLabels[sameGroupLabels.length - 1];
          const minGap = 5;
          const overlapOffset = lastLabel.x + lastLabel.width + minGap - targetX;
          if (overlapOffset > 0) {
            targetX += overlapOffset;
          }
        }

        sameGroupLabels.push({ x: targetX, width: labelWidth });
        return { x: targetX, y: targetY };
      },
      labelLine: { show: true, lineStyle: { color: '#bbb' } },
      label: {
        show: true,
        formatter: (param: any) => param.data[3],
        position: 'right',
        minMargin: 2
      }
    }
  ]
};

方案说明

  • 通过groupLabelInfo对象隔离两组标签的位置数据,避免跨区域干扰
  • 自定义minGap参数可调整标签间的最小间距,保证可读性
  • 仅对同组标签进行重叠检测,相比全局配置提升了性能
  • 保留了原需求中标签固定在顶部/底部区域的布局逻辑

内容的提问来源于stack exchange,提问作者bleujaiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 03:04:51