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 实例 同样是渲染完成事件,当动画或渐进渲染结束时触发

上表只摘录行为事件的关键部分,更详细的介绍请参考 官网文档

行为事件的发生代表着组件实体内部状态发生了某些变更,有两种原因可能触发行为事件:

  1. 用户交互行为,例如图例组件中,用户通过鼠标点击切换图例开关时,ECharts 除触发鼠标 click 事件外,还会触发 legendselectchanged 行为事件;
  2. 接口调用,例如图例组件中,调用 echartInstance.dispatchAction({ type: 'legendToggleSelect' }) 后也依然会触发 legendselectchanged 行为事件。

2.2 事件源

事件源描述了触发事件的主体,对于鼠标事件,事件源通常是行为发生时鼠标焦点所在图形区域对应的图表。所有类型的图表都支持鼠标事件;部分组件支持触发鼠标事件,但默认是关闭的,需要通过设置 triggerEvent: true 来启动。组件对鼠标事件的支持情况如下:

  • 支持:titlexAxisyAxisradiusAxisangleAxisradarparallelAxissingleAxistimelinecalendar
  • 不支持: polarlegendgriddatazoomvisualMaptooltipaxisPointertoolboxbrushgeoparallelgraphic

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 上触发时有如下属性:

componentTypecomponentSubTypecomponentIndexseriesTypeseriesIndexseriesIdseriesNamenamedataIndexdatadataTypevaluecolorborderColordimensionNamesencodemarker$varseventtype

yAxis 则有:

componentTypecomponentIndexyAxisIndextargetTypevalueeventtype

title 上则是:

componentTypecomponentIndexeventtype

Tips
遗憾的是,官网并未就此给出详细、完整的列表,建议开发时通过 console.logdebugger 等手段获取各种组件所传递的事件参数。

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
需要注意,所谓的全局监听并不是所有组件的交互行为都可以被监听,对于鼠标事件,需要满足如下条件:

  1. 组件本身支持鼠标交互事件,详情可参考 2.2 事件源 一节。
  2. 组件启用了鼠标事件功能,所有图表默认启用;其他组件则需要设置 triggerEvent: true 显式声明,如上例的 titleyAxisxAxis 组件。

在回调函数中,可以通过回调参数的 componentTypecomponentSubType 等属性事件发生的具体位置,详情可参考 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 的事件系统设计的比较隐晦,有很多隐藏逻辑并没有表现在官方文档上,本文尝试对事件系统做个全局的介绍,总结重点如下:

  1. ECharts 事件包括鼠标事件、交互事件两种类型;
  2. 所有图表组件都支持鼠标事件;部分组件支持鼠标事件,但需要设置 triggerEvent: true 显式声明启动鼠标组件支持;
  3. 监听函数 on 默认监听实例上所有的组件,可通过 query 过滤事件源,不过 query 参数只对鼠标事件有效;
  4. 对于鼠标事件,种类型的事件在不同组件触发时,事件参数不同,且目前官方未提供详尽的说明文档,只能又开发者自行摸索;
  5. 同一组件的所有鼠标事件的事件参数相同。

6. 小结

图片描述
本节完整地介绍 Echarts 中事件系统的设计理念与用法,包括事件名称、事件源、事件参数三类要素的意义;绑定、解绑事件的接口;官方事件,以及如何自定义事件等。