ECharts 事件系统
当我们的图表变得越来越庞大之后,我们加入的组件也会越来越多,所以我们之后做的不单单只有 “看”这一个动作,还要有其他的动作,对应的就是一个个事件的处理,用户操作触发事件,我们则可以通过监听事件并处理来完成一系列的事件操作,所以这一节我们就来从事件三要素,事件绑定,事件解绑这几个方面去了解一下 ECharts 的事件系统。
1. 简介
在 ECharts 的图表中用户的操作将会触发相应的事件。开发者可以监听这些事件,然后通过回调函数做相应的处理,比如跳转到一个地址,或者弹出对话框,或者做数据下钻等等。更多细节可参考 官网。
慕课解释
ECharts 开放了两套 API 体系,一是 ECharts 类接口及实例的接口,例如常用的 echarts.init
方法、echartInstance.setOption()
;二是围绕事件展开的动态交互接口,包括用于监听事件的 echartInstance.on
函数和用于触发行为的 echartInstance.dispatchAction
函数。
本文讨论使用 echartInstance.on
接口实现的事件监听功能。
2. 事件三要素
与 Dom Event 规范 类似,ECharts 通过事件名称、事件源、事件参数三个要素精确描述谁在何处执行了何种操作。在展开示例讨论前,有必要简单讨论下 ECharts 事件三要素的含义。
2.1 事件名称
ECharts 中存在两种类型的事件,第一种是鼠标在图形示例上的行为所触发的鼠标事件,包括:
上述事件除 globalout 外,均与 DOM Event 规范 定义的同名事件有相同的语义、触发条件。globalout 在鼠标移出图表示例范围时触发。
第二种称为行为事件,在组件、图表状态发生某种业务状态迁移时触发,包括:
事件名 | 适用组件 | 触发时机 |
---|---|---|
legendselectchanged | legend | 切换图例选中状态后的事件 |
legendselected | legend | 图例选中后的事件 |
legendunselected | legend | 图例取消选中后的事件 |
legendscroll | legend | 图例滚动事件 |
datazoom | datazoom | 数据区域缩放后的事件 |
datarangeselected | visualMap | 视觉映射组件中,range 值改变后触发的事件 |
timelinechanged | timeline | 时间轴中的时间点改变后的事件 |
timelineplaychanged | timeline | 时间轴中播放状态的切换事件 |
dataviewchanged | toolbox | 工具栏中数据视图的修改事件 |
magictypechanged | toolbox | 工具栏中动态类型切换的切换事件 |
brush | brush | 选框添加事件 |
globalcursortaken | brush | brush 组件捕获鼠标 cursor 时触发 |
brushselected | brush | 选框内容变更事件 |
geoselectchanged | geo | geo 中地图区域切换选中状态的事件 |
geoselected | geo | geo 中地图区域选中后的事件 |
geounselected | geo | geo 中地图区域取消选中后的事件 |
axisareaselected | 平行坐标轴 | 平行坐标轴范围选取事件 |
pieselectchanged | 饼图 | 饼图扇形切换选中状态的事件 |
pieselected | 饼图 | 饼图扇形选中后的事件 |
pieunselected | 饼图 | 饼图扇形取消选中后的事件 |
mapselectchanged | 地图 | 地图区域切换选中状态的事件 |
mapselected | 地图 | 地图区域选中后的事件 |
mapunselected | 地图 | 地图区域取消选中后的事件 |
focusnodeadjacency | 连接图 | graph 图邻接节点高亮事件 |
unfocusnodeadjacency | 连接图 | graph 的邻接节点取消高亮事件 |
restore | ECharts 实例 | 重置 option 事件 |
rendered | ECharts 实例 | 渲染完成事件 |
finished | ECharts 实例 | 同样是渲染完成事件,当动画或渐进渲染结束时触发 |
上表只摘录行为事件的关键部分,更详细的介绍请参考 官网文档。
行为事件的发生代表着组件实体内部状态发生了某些变更,有两种原因可能触发行为事件:
- 用户交互行为,例如图例组件中,用户通过鼠标点击切换图例开关时,ECharts 除触发鼠标 click 事件外,还会触发 legendselectchanged 行为事件;
- 接口调用,例如图例组件中,调用
echartInstance.dispatchAction({ type: 'legendToggleSelect' })
后也依然会触发 legendselectchanged 行为事件。
2.2 事件源
事件源描述了触发事件的主体,对于鼠标事件,事件源通常是行为发生时鼠标焦点所在图形区域对应的图表。所有类型的图表都支持鼠标事件;部分组件支持触发鼠标事件,但默认是关闭的,需要通过设置 triggerEvent: true
来启动。组件对鼠标事件的支持情况如下:
- 支持:
title
,xAxis
,yAxis
,radiusAxis
,angleAxis
,radar
,parallelAxis
,singleAxis
,timeline
,calendar
; - 不支持:
polar
,legend
,grid
,datazoom
,visualMap
,tooltip
,axisPointer
,toolbox
,brush
,geo
,parallel
,graphic
。
Tips:
graphic 是原生图形组件,不支持
echartInstance.on
接口,但可直接调用element.onclick
等接口实现事件监听。
行为事件由特定的组件、图表触发,例如 legendselectchanged 的事件源只能是 legend 组件,更多信息请参考 事件名称 一节。
2.3 事件参数
事件参数描述事件发生时的上下文信息,ECharts中不同事件的参数信息相差极大,甚至同种事件在不同组件触发时,回调参数也有差异。
2.3.1 鼠标事件参数
ECharts 鼠标事件,虽然名称上与 DOM Event 规范 一致,但回调中传递的参数比标准相差很大。以 click 为例,DOM 的 click 事件参数是一个 MouseEvent 对象,主要属性有:
{
isTrusted: boolean,
screenX: number,
screenY: number,
clientX: number,
clientY: number,
ctrlKey: boolean,
shiftKey: boolean,
altKey: boolean,
metaKey: boolean,
relatedTarget: object,
pageX: number,
pageY: number,
x: number,
y: number,
offsetX: number,
offsetY: number,
...
}
可以看出 DOM 的 click 事件参数详细描述了点击行为发生的位置、事件源的 dom、是否带有快捷键、捕获的阶段等。而 ECharts 在 series 上发生的 click 事件带有如下参数:
{
// 当前点击的图形元素所属的组件名称,
// 其值如 'series'、'markLine'、'markPoint'、'timeLine' 等。
componentType: string,
// 图形元素所属二级组件类型
// 如 `bar`、`line`、`pie` 等
componentSubType: string,
componentIndex: number,
// 系列类型。值可能为:'line'、'bar'、'pie' 等
seriesType: string,
// 系列在传入的 option.series 中的 index
seriesIndex: number,
// 系列ID
seriesId: string,
// 系列名称
seriesName: string,
// 数据名,类目名
name: string,
// 触发事件的数据在data数组中的index
dataIndex: number,
// 触发事件的数据所传入的原始data值
data: number,
// sankey、graph 等图表同时含有 nodeData 和 edgeData 两种 data,
// dataType 的值会是 'node' 或者 'edge',表示当前点击在 node 还是 edge 上。
// 其他大部分图表中只有一种 data,dataType 无意义。
dataType: string,
// 传入的数据值
value: number | Array,
// 数据图形的颜色
color: string,
// 数据图形的边框色
borderColor: undefined,
// 数据图形的维度信息
dimensionNames: object,
encode: object,
// 标记信息的html内容
marker: string,
$vars: object,
// 原始click事件参数
event: object,
// 事件名称,本例中为 `click`
type: string,
}
可以看出,ECharts 传递的 click 事件参数侧重于描述发生点击行为的图形所对应的组件信息、状态、配置,比如上例中的 componentType、componentSubType 指明单击的组件类别、子类别;seriesType、seriesIndex、data 等指明单击组件所对应的数据配置值;marker、encode 则指明单击发生时,组件内部状态信息。大多数情况下这些信息是足够使用的,必要时也可以通过 event 属性读取原始 dom 事件参数。
需要注意的第二点是,即使是同种事件,不同组件所暴露的参数也是不一样的,以 click 为例,在 series.bar
上触发时有如下属性:
componentType
、componentSubType
、componentIndex
、seriesType
、seriesIndex
、seriesId
、seriesName
、name
、dataIndex
、data
、dataType
、value
、color
、borderColor
、dimensionNames
、encode
、marker
、$vars
、event
、type
在 yAxis
则有:
componentType
、componentIndex
、yAxisIndex
、targetType
、value
、event
、type
在 title
上则是:
componentType
、componentIndex
、event
、type
Tips:
遗憾的是,官网并未就此给出详细、完整的列表,建议开发时通过console.log
、debugger
等手段获取各种组件所传递的事件参数。
2.3.2 行为事件参数
与鼠标事件参数一样,行为事件也没有提供一致的参数模型,不过官网提供了 明细说明,开发时建议前往查阅。
3. 监听事件
ECharts 中可通过 echartInstance.on
函数绑定事件处理函数,on 函数签名:
(eventName: string, query?: string|Object, handler: Function, context?: Object)
各参数说明:
参数名 | 类型 | 必选 | 说明 |
---|---|---|---|
eventName | string | 是 | 指定监听的事件名称 |
query | string|object | 否 | 指定在特定的组件或者元素上响应 ,仅在鼠标事件中有效 |
handler | function | 是 | 事件回调函数 |
context | object | 否 | 回调函数执行时的 this 对象,默认为触发事件的 ECharts 实例对象 |
3.1 全局监听
若未提供 query 参数,ECharts 将不对事件源做任何过滤,相当于注册了一个全局事件回调。例如:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Echarts Example</title>
</head>
<body>
<div id="main" style="width: 600px;height: 400px"></div>
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.common.js"></script>
<script type="text/javascript">
const myChart = echarts.init(document.getElementById('main'));
const option = {
title: {
text: 'test',
// 通过 triggerEvent 显式声明
// 该组件将触发事件回调
triggerEvent: true,
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
triggerEvent: true,
},
yAxis: {
type: 'value',
triggerEvent: true,
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'bar',
},
],
};
myChart.setOption(option);
// 注册 `click` 事件回调
myChart.on('click', function (e) {
console.log(`click invoke at ${e.componentType}`);
});
</script>
</body>
</html>
示例效果:
Tips:
需要注意,所谓的全局监听并不是所有组件的交互行为都可以被监听,对于鼠标事件,需要满足如下条件:
- 组件本身支持鼠标交互事件,详情可参考 2.2 事件源 一节。
- 组件启用了鼠标事件功能,所有图表默认启用;其他组件则需要设置
triggerEvent: true
显式声明,如上例的title
、yAxis
、xAxis
组件。
在回调函数中,可以通过回调参数的 componentType
、componentSubType
等属性事件发生的具体位置,详情可参考 2.3 事件参数 一节。
3.2 带过滤条件的监听
若提供了 query 参数,则 ECharts 在执行回调前,会先判断事件源是否满足过滤条件。 query 参数支持 string、object 两种形式,当使用字符串时,内容格式可以是 mainType、mainType.subType 两种形式,例如:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Echarts Example</title>
</head>
<body>
<div id="main" style="width: 600px;height: 400px"></div>
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.common.js"></script>
<script type="text/javascript">
const myChart = echarts.init(document.getElementById('main'));
const option = {
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] },
yAxis: { type: 'value', min: 800 },
series: [
{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'bar' },
{ data: [920, 1032, 1001, 1034, 1390, 1430, 1420], type: 'line' },
],
};
myChart.setOption(option);
// 在所有 series 上都触发
myChart.on('click', 'series', function (e) {
console.log(`series listener: click invoke at ${e.componentType}.${e.componentSubType}`);
});
// 只在 line 图表上触发
myChart.on('click', 'series.line', function (e) {
console.log(`line listener: click invoke at ${e.componentType}.${e.componentSubType}`);
});
</script>
</body>
</html>
示例有两个 click 回调,第一个指定过滤参数为 series,将在所有图表发生单击事件时执行回调;第二个指定过滤参数为 series.line
,则只在折线图发生单击事件时触发。示例效果:
query 还可以以对象方式传入,对象可以包含如下属性:
{
<mainType>Index: number // 组件 index
<mainType>Name: string // 组件 name
<mainType>Id: string // 组件 id
dataIndex: number // 数据项 index
name: string // 数据项 name
dataType: string // 数据项 type,如关系图中的 'node', 'edge'
element: string // 自定义系列中的 el 的 name
}
其中 mainType 为组件类型,如 seriesIndex、xAxisIndex 等。示例:
const option = {
...
series: [
{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'bar', name: 'series1' },
{ data: [920, 1032, 1001, 1034, 1390, 1430, 1420], type: 'line', name: 'series2' },
],
};
myChart.on(
'click',
// 在 series1 上触发
{seriesName:'series1'},
function(e) {
}
);
字符串与对象形式过滤的功能不同,字符串形式只能根据组件类型、子类型过滤;对象形式则精确到组件、数据项维度。继续看看示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Echarts Example</title>
</head>
<body>
<div id="main" style="width: 600px;height: 400px"></div>
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
<script type="text/javascript">
const myChart = echarts.init(document.getElementById('main'));
const option = {
grid: { triggerEvent: true },
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
yAxis: {
type: 'value',
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'bar',
},
],
title: { text: 'test' },
};
myChart.setOption(option);
myChart.on('click', { dataIndex: 1 }, (e) => {
console.log(`click invoke with data index: ${e.dataIndex}`);
});
</script>
</body>
</html>
示例声明过滤参数为 { dataIndex: 1 }
,则只会在数据项 1 上触发,效果:
4. 解绑事件监听
可通过 echartInstance.off
接触事件绑定,函数签名:
(eventName: string, handler?: Function)
handler 参数可选,若未提供该参数则解除所有 eventName 的事件监听。
5. 个人经验
ECharts 的事件系统设计的比较隐晦,有很多隐藏逻辑并没有表现在官方文档上,本文尝试对事件系统做个全局的介绍,总结重点如下:
- ECharts 事件包括鼠标事件、交互事件两种类型;
- 所有图表组件都支持鼠标事件;部分组件支持鼠标事件,但需要设置
triggerEvent: true
显式声明启动鼠标组件支持; - 监听函数 on 默认监听实例上所有的组件,可通过 query 过滤事件源,不过 query 参数只对鼠标事件有效;
- 对于鼠标事件,种类型的事件在不同组件触发时,事件参数不同,且目前官方未提供详尽的说明文档,只能又开发者自行摸索;
- 同一组件的所有鼠标事件的事件参数相同。
6. 小结
本节完整地介绍 Echarts 中事件系统的设计理念与用法,包括事件名称、事件源、事件参数三类要素的意义;绑定、解绑事件的接口;官方事件,以及如何自定义事件等。