Skip to content
交互组件:tooltip、legend、dataZoom 与 visualMap
ECharts 的 option 定义了图表的静态形态,而交互组件把这幅图变成用户可以推拉、筛选、聚焦的数据探索界面。同一份数据,搭配不同的组件组合,给用户的感受完全不同。
概述
交互组件是 ECharts 中独立于 series 的配置单元,它们不产生数据图形,而是控制用户如何观察和操作数据。本篇覆盖四个核心交互组件:
- tooltip:悬浮提示框,展示数据项的详细信息。
- legend:图例,同时也是系列可见性的开关。
- dataZoom:数据窗口缩放,在数据层面做过滤而非视觉裁剪。
- visualMap:视觉映射,将数值映射到颜色、大小等视觉通道。
这四个组件可以独立工作,也可以在同一个图表中协同使用。
tooltip:触发模式与内容控制
基本概念
tooltip 是用户与数据点之间最直接的交互通道。当鼠标悬停或点击数据项时,tooltip 弹出包含该数据项详细信息的浮层。
trigger 模式
tooltip.trigger 决定提示框何时出现,可选值有三:
'item':悬浮在数据项上触发。饼图、散点图等无轴图表的默认行为。每个数据项独立触发。'axis':基于坐标轴位置触发。直角坐标系中折线图、柱状图的默认模式。在同一条竖直或水平线上,会同时显示所有系列在该位置的数据。'none':完全关闭悬浮提示。
当 trigger: 'axis' 与 tooltip.axisPointer 配合时,可以显示跨轴的交叉指示线,用户能直观地追踪数值对应的坐标位置。
自定义内容
javascript
tooltip: {
trigger: 'axis',
formatter(params) {
// params 是一个数组,每项对应一个系列在当前位置的信息
return params
.map(item => `${item.seriesName}:${item.value}℃`)
.join('<br/>');
}
}formatter 支持字符串模板和回调函数两种形式。模板变量 {a}-{e} 分别对应系列名、数据名、数值等字段。更精细的控制需要用回调函数,回调的 params 参数在 trigger: 'axis' 时是一个数组,每个元素代表一个系列在当前位置的信息。
回调函数每次触发都会执行。数据量大且 tooltip 频繁刷新时,复杂的计算可能带来可感知的延迟。
ECharts 5.3.0 新增了 tooltip.valueFormatter,直接在数值片段上做格式化,不必重写整个提示框就能处理小数位、单位等需求:
javascript
tooltip: {
valueFormatter: value => value.toFixed(2) + ' ms'
}散点图、双轴图这类数值差异大的场景,用 valueFormatter 比整体重写 formatter 更简洁。
注意点
在 v5.x 早期版本中,鼠标悬停在图例项时触发的 tooltip 会继承外层 tooltip.formatter 配置,导致图例 hover 时出现不符合预期的文本内容。这个行为在后续版本中已修复。如果在使用时发现图例 hover 时 tooltip 内容异常,检查 ECharts 版本即可。
legend:数据筛选器
基本概念
图例不只是颜色标签。在 ECharts 中,legend 是一个交互式数据过滤器——点击图例项会切换对应系列的显示和隐藏,并且自动触发图表重绘。legend.data 显式指定要显示的系列名称数组,与 series 中的 name 字段对应。
选择模式
selectedMode 控制图例项的选择行为:
false:不可选,图例退化为纯标识。'single':单选模式,同时只能有一个系列可见。'multiple'(默认):多选模式,点击切换每个系列的可见性。'series'(5.3.0 新增):直接触发整个系列的选中状态,不进入单个数据项的高亮。
初始化时可能希望部分系列默认隐藏,通过 legend.selected 对象指定:
javascript
legend: {
data: ['温度', '湿度', '气压'],
selected: { '温度': false }
}这段配置让"温度"系列在图表初始渲染时处于隐藏状态,用户可以通过点击图例手动打开。
滚动图例
当 type: 'scroll' 且图例项数量超出容器范围时,图例区域支持鼠标滚轮滚动。滚动行为会触发 legendscroll 事件,事件对象中包含 scrollDataIndex 和 legendId,可以据此获取当前滚动位置。
dataZoom:数据窗口缩放
工作原理
dataZoom 在数据层面进行过滤,而不是简单的视觉裁剪。它的运行原理是:只绘制落在数据窗口内的数据点,窗口外的数据点不参与渲染。这个行为直接影响轴刻度范围、tooltip 显示的数据、以及 visualMap 的映射范围。
两种操作类型
dataZoom 通过组件类型区分操作方式:
dataZoomSlider:独立的滑动条组件,用户拖拽滑块或两端手柄调整窗口。通过start和end设定初始比例(0-100 的百分比)。dataZoomInside:内置于坐标系中,通过鼠标滚轮缩放、拖拽平移来调整窗口,没有独立的 UI 控件。
javascript
dataZoom: [
{ type: 'slider', xAxisIndex: 0, start: 20, end: 80 },
{ type: 'inside', xAxisIndex: 0 }
]这个配置在 x 轴上同时设置了 slider 和 inside 两种操作方式,初始窗口为 20%-80%。多个 dataZoom 控制同一根轴时会自动联动——拖动 slider 会使 inside 的窗口同步变化,反之亦然。
轴绑定与范围设定
xAxisIndex 和 yAxisIndex 指定 dataZoom 控制哪些轴,缺省情况下会挂到第一个 x 轴(直角坐标系)或 y 轴(单轴)。初始窗口除了用百分比 start/end,也可以用绝对值 startValue/endValue 指定。
事件
用户操作 dataZoom 结束后触发 datazoom 事件。事件对象中 start 和 end 是百分比值,startValue 和 endValue 仅在工具栏缩放行为的事件中存在。需要同步其他视图时(比如多个图表联动缩放),可以监听这个事件获取当前窗口信息。
visualMap:视觉编码映射
基本概念
visualMap 将数据的一个维度映射到视觉通道——颜色、大小、透明度、亮度等。它不做数据过滤,只负责外观映射范围。visualMap 有两种类型:连续型(continuous)和分段型(piecewise)。
continuous 型
线性映射。通过 inRange 定义有效数据区间内的视觉表现,outOfRange 定义区间外的表现:
javascript
visualMap: {
type: 'continuous',
min: 0,
max: 100,
inRange: { color: ['#50a3ba', '#eac736', '#d94e5d'] },
outOfRange: { opacity: 0.3 }
}数值 0 映射到第一个颜色,100 映射到最后一个颜色,中间值通过插值计算;超出 0-100 范围的数据点透明度降为 0.3。
piecewise 型
分段映射,每一段指定独立的视觉属性:
javascript
visualMap: {
type: 'piecewise',
categories: ['低速', '中速', '高速'],
inRange: { color: ['#66b032', '#fbbf24', '#c1121f'] }
}每个分类直接对应一个颜色,无渐变过渡。适合离散分类数据。
视觉通道
inRange 和 outOfRange 可以同时设置多个视觉通道:color、symbolSize、opacity、colorAlpha 等。例如在散点图上同时映射颜色和大小:
javascript
visualMap: {
type: 'continuous',
min: 0,
max: 500,
inRange: {
color: ['#313695', '#4575b4', '#fdae61', '#a50026'],
symbolSize: [5, 30]
}
}数据值越大,点的颜色越偏暖,尺寸越大。多个通道同时映射可以让数据分布更直观。
四组件协同示例
下面是一个直角坐标系双系列散点图,四个交互组件同时生效。数据模拟了不同类别在不同压力下的响应时间。
javascript
const option = {
title: { text: '系统负载与延时分布' },
tooltip: {
trigger: 'item',
formatter({ seriesName, value }) {
return `${seriesName}<br/>并发:${value[0]}<br/>延时:${value[1]} ms`;
}
},
legend: {
data: ['读取', '写入'],
selected: { '写入': false } // 初始只展示读取系列
},
xAxis: { name: '并发数', type: 'value' },
yAxis: { name: '延时 (ms)', type: 'value' },
dataZoom: [
{ type: 'slider', xAxisIndex: 0, start: 0, end: 60 },
{ type: 'inside', xAxisIndex: 0 }
],
visualMap: {
type: 'continuous',
min: 0,
max: 1200,
inRange: {
color: ['#4575b4', '#fee090', '#d73027'],
symbolSize: [4, 20]
},
outOfRange: { opacity: 0.2 }
},
series: [
{
name: '读取',
type: 'scatter',
data: [/* ...大量点 */]
},
{
name: '写入',
type: 'scatter',
data: [/* ... */]
}
]
};组件间的关系如下:
- dataZoom 把初始 x 轴窗口定在前 60%,超出窗口的数据点在画布上不渲染。需要注意,窗口外的数据也不参与 visualMap 的范围计算——visualMap 基于全量数据的 min/max(此处为 0-1200),不会随缩放窗口动态调整。
- visualMap 的颜色和大小映射覆盖 0-1200 的全局范围,当前可见窗口内的数据点按全局映射着色。
- legend 初始只显示"读取"系列,用户可手动打开"写入"。
- tooltip 在每次悬浮时通过回调拼接文本。散点图默认
trigger: 'item',每个数据点独立触发。
不同坐标系下的组件适配
dataZoom 在设计上绑定在直角坐标系(grid)和极坐标系(polar)上。如果 series 使用地理坐标系(geo 组件),例如地图上的散点图,dataZoom 不会生效。
对于地图场景,颜色映射仍可通过 visualMap 实现:
javascript
visualMap: {
type: 'continuous',
min: 0,
max: 50000,
inRange: { color: ['#e0f3db', '#43a2ca', '#0868ac'] }
},
geo: { map: 'china', roam: true },
series: [{
type: 'scatter',
coordinateSystem: 'geo',
data: [/* 经纬度数据 */]
}]roam: true 允许用户缩放和平移地图,但这是由 geo 组件提供的图形变换,不是 dataZoom 的数据窗口过滤。两者行为完全不同:geo 的缩放是视图变换,所有数据点始终可见;dataZoom 是数据裁剪,窗口外的数据点被移除。
组件联动的注意点
visualMap 与 dataZoom 的映射范围
dataZoom 缩小窗口后,可能期望 visualMap 的颜色映射基于当前可见数据重新计算,让局部分布差异更明显。但默认实现中,visualMap 的 min/max 来自初始全集数据,不会随 dataZoom 变化自动更新。
结果:把数据窗口拖到低数值区域时,所有点可能都显示为浅蓝色,视觉区分度差。
缓解方式:
- 监听
datazoom事件,拿到start/end百分比后推算新的 min/max,再通过setOption更新visualMap。这要求本地持有完整数据并能快速计算,且频繁更新可能带来性能压力。 - 将 visualMap 应用在 dataZoom 过滤后的数据上——但这也意味着放弃 ECharts 内置映射,自行处理颜色和尺寸计算。
在设计同时使用 dataZoom 和 visualMap 的图表时,需要明确告知用户颜色映射对应的是全局范围,或者在交互上做取舍。
高亮性能
tooltip 悬浮和图例联动都会触发高亮动画。散点数量达到万级时,高亮的图形重绘可能导致交互明显延迟。ECharts 5.3.0 提供了 emphasis.disabled: true 直接关闭高亮状态。同时 select.disabled 可以对部分数据关闭选中动画。
在大数据量散点图中,关掉高亮和选中,鼠标悬浮只靠 tooltip 文本反馈,交互会流畅很多。
filterMode 的影响
dataZoom 的 filterMode 默认 'filter',会从 series 数据中剔除不在窗口内的数据项。如果图表上配置了 label 或 markLine,过滤后这些附加元素也可能消失。某些场景可能需要 filterMode: 'empty',让数据位置保留但图形不绘制,需要根据具体需求测试确定。
事件更新循环
在外部系统中监听 datazoom 事件或 datarangeselected 事件时,如果处理逻辑中再次调用 setOption 并修改了数据或映射,需要避免死循环。例如监听 datazoom 去更新 visualMap,而 visualMap 变化可能触发数据重绘——虽然不一定会再次触发 datazoom,但在设计联动逻辑时需要留意这条链路。
