Skip to content

ECharts 折线图线段点击

ECharts 折线图的 click 事件默认只在拐点(Symbol)上触发。两个拐点之间的连线本身不会直接响应点击,这会限制“点击线上任意位置查看该时间点数据”这类交互。


根因:事件代理层级

ECharts 的事件系统建立在 ZRender 之上。ECharts 的 chart.on('click', ...) 只在 ECharts 层面注册——它只对"有独立图形元素"的组件响应。折线图的拐点(Symbol)是独立的 ZRender 图形元素,而线段是 ec-polyline 类型——ECharts 没有为它暴露事件。

ZRender 本身会对所有图形元素触发事件。解决方案是跳过 ECharts 的事件层,直接在 ZRender 层监听

js
const chart = echarts.init(dom);

// 获取底层 ZRender 实例
const zr = chart.getZr();

zr.on('click', (e) => {
  // e.target 是 ZRender 的图形元素
  if (e.target && e.target.__ec_inner_1) {
    const seriesIndex = e.target.__ec_inner_1;
    // 通过 seriesIndex 定位到被点击的折线系列
  }
});

内部属性的版本兼容风险

__ec_inner_1 是 ECharts 内部属性,命名中的下划线前缀表明它不是公开 API。在不同 ECharts 版本中:

  • ec-polyline 元素可能被重构为多个子元素
  • __ec_inner_1 的命名可能变化(__ec_inner_0__ec_inner_2 等其他索引也存在)
  • 属性携带的数据结构可能调整

这是绕过公开 API 的代价。实际接入时通常需要补充以下约束:

  1. 锁定 ECharts 版本号(不使用 ^ 范围)
  2. 在 CI 中加 ECharts 版本升级的 snapshot 测试,验证 __ec_inner 属性未变化
  3. 代码中加防御性检查:if (e.target?.type === 'ec-polyline' && typeof e.target.__ec_inner_1 === 'number')

完整实现

js
chart.getZr().on('click', (e) => {
  const target = e.target;

  // 仅处理折线线段点击(排除拐点 Symbol 和区域填充)
  if (!target || target.type !== 'ec-polyline') return;

  // 获取 series 下标
  const seriesIndex = target.__ec_inner_1;
  if (typeof seriesIndex !== 'number') return;

  // 通过点击坐标反查最近的数据点
  const pointInPixel = [e.offsetX, e.offsetY];
  const pointInGrid = chart.convertFromPixel({ seriesIndex }, pointInPixel);

  // 在数据中查找最近的 x 轴索引
  const data = chart.getOption().series[seriesIndex].data;
  // ... 根据 pointInGrid 找到最近的数据点
});

convertFromPixel 是 ECharts 的公开 API,将像素坐标转换为图表坐标系中的值。结合 ZRender 的精确点击坐标,可以实现"点击线上任意位置获取该位置对应的数据"。


同模式:区域填充(area)的点击

对于带区域填充的折线图(areaStyle),填充区域的 ZRender type 是 ec-polygon,同样可以通过 __ec_inner 属性获取 seriesIndex:

js
zr.on('click', (e) => {
  if (e.target?.type === 'ec-polygon') {
    const seriesIndex = e.target.__ec_inner_1;
    // 同折线线段的处理逻辑
  }
});

填充区域的点击范围比线段大得多——这提升了移动端上的可点性。


已知限制

一个未解决的 bug:主题切换时,在特定的 width 值下点击事件会消失,换一个值则恢复。推测根因是 ZRender 在 resize 时对 ec-polyline 元素的命中检测(hit test)计算与 CSS 尺寸存在 1px 级的舍入误差,导致特定宽度下线段宽度为 0.5px 以下时为不可命中状态。临时规避:设置 series.lineStyle.width >= 1 或在主题切换后手动调用 chart.resize()

如果需要在不同 ECharts 版本或复杂交互中维持稳定性,可以回到 ECharts 公开 API:在 Symbol 上绑定事件,通过 seriesIndex + dataIndex 定位数据。线段点击更适合作为增强交互,而不是核心依赖。