您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

ECharts 创建图表

经过前面的学习,相信大家已经在自己的项目中绘制了基本的 ECharts 图表,但是前面学习只是为了让我们更好的了解安装的流程。所以该小节,我们将会去更加详细的了解到 ECharts 的 创建更新,也会通过案例去跟踪分析整个过程,所谓 万事开头难,个过程中也会有许许多多需要同学们注意的地方,希望大家在学习的时候也能静下心来完成这重要的一步。

ECharts 非常简单,定义图表的基本流程是实例化 echarts 后, setOption 传入配置对象,ECharts 根据配置对象的描述渲染各类图表、组件。本节主要讲解初始配置图表的基本流程,以及实例化后动态更新图表的。

通常,创建 ECharts 图表需要执行如下步骤:

例如:

<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		< charset="utf-8" />
		< http-equiv="X-UA-Compatible" content="IE=edge" />
		< name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<!-- 第一步,设定图表容器 DOM 节点 -->
		<div id="main" style="width: px; height: px;"></div>

		<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
		<script type="text/javascript">
			// 第二步, init 初始化echarts对象
			const myChart = echarts.init(document.getElementById('main'));

			const option = {
				tool@R_913_2@: { feature: { saveAsImage: {} } },
				xAxis: {
					type: 'category',
					data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
				},
				yAxis: { type: 'value' },
				series: [
					{
						type: 'line',
						data: [, , , , , , ],
					},
				],
			};
			// 第三步, setOption 设定图表配置
			myChart.setOption(option);
		</script>
	</body>
</html>

示例:

图表容器通常使用 div 定义, init 后 ECharts 会在节点中插入多个 canvas 或 svg ,用以渲染图表。容器节点必须具备初始宽高,常用如下形式定义:

<!-- 使用像素定义宽高 -->
<div id="main" style="width: px; height:px"></div>
<!-- 也使用百分比定义宽高 -->
<div id="main" style="width: ; height:px"></div>

ECharts 3 之后直接使用 canvas 元素作为容器,这样绘制完图表可以直接将 canvas 作为应用到其它地方,例如在 WebGL 中作为贴图,这跟使用 echartsInstance.getDataURL 相比可以图表的实时刷新。

但缺点是无法根据组件的 z 将组件分离渲染到不同的 canvas 上,所以有一定的损失。

echarts.init 用于创建 ECharts 对象,不能在同容器上多次,签名:

(dom: HTMLDivElement|HTMLCanvasElement, theme?: Object|string, opts?: {
    devicePixelRatio?: number
    renderer?: string
    width?: number|string
    height? number|string
}) => ECharts

参数:

{
	// 设备像素比,认取浏览器的值 `window.devicePixelRatio`
	devicePixelRatio: Number,
	// 渲染器, canvas 或 svg
	renderer: String,
	// 可显式指定实例宽度,单位为像素。如果传入值为 null/undefined/'auto',则取 dom(实例容器)的宽度。
	width: Number|String|null|undefined,
	// 可显式指定实例高度,单位为像素。如果传入值为 null/undefined/'auto',则取 dom(实例容器)的高度。
	height: Number|String|null|undefined,
}

容器的宽高应通过节点的样式定义,尽量避免通过 opts 定义,这样在 init 前后,作为容器的 DOM 的尺寸才能保持一致。

实例化 ECharts 对象后,需 echartInstance.setOption 接口传入图表配置,接口签名:

(option: Object, notMerge?: boolean, lazyUpdate?: boolean) => void
// 或
(option: Object, opts?: Object) => void

第一种形态的参数为:

第二种形态的 opts 参数接受如下对象:

{
	// 认 `false`,用于设定是否与之前提供的配置合并
	notMerge: ...,
	// 认 `false`,是否延迟更新
	lazyUpdate: ...,
	// 认 `false`,指定图表更新时是否触发事件
	silent: ...
}

其中 option 对象是必不可少的,用于描述开发者对图表的各项需求,使用什么组件、有什么数据、使用什么图表、图表有哪些操作等等。

ECharts 是配置驱动的图表框架,主要都以配置对象形式声明,某种程度上可以说 ECharts 的应用都是围绕配置对象展开的。配置对象结构如下:

{
		title: object,
		legend: object,
		grid: object,
		xAxis: object,
		yAxis: object,
		polar: object,
		radiusAxis: object,
		angleAxis: object,
		radar: object,
		dataZoom: array,
		visualMap: array,
		tooltip: object,
		axisPointer: object,
		tool@R_913_2@: object,
		brush: object,
		geo: object,
		parallel: object,
		parallelAxis: object,
		singleAxis: object,
		timeline: object,
		graphic: object,
		calendar: object,
		dataset: object,
		aria: object,
		series: array,
		color: array,
		backgroundColor: string,
		textStyle: object,
		animation: boolean,
		animationThreshold: number,
		animationDuration: number,
		animationEasing: string,
		animationDelay: number,
		animationDurationUpdate: number,
		animationEasingUpdate: string,
		animationDelayUpdate: number,
		blendMode: string,
		hoverLayerThreshold: number,
		useUTC: boolean,
}

ECharts 的使用绝大部分都围绕着这些展开。根据配置作用,可以将上述划分为以下几类:

ECharts 的图表由 series 数组指定。series 数组项接受对象形式,每个数组项对应图表,数组项通过 type 声明图表类型;通过 data 数组声明图表数据序列。比如:

series: [
	{
		data: [, , , , , , ],
		type: 'bar',
	},
	{
		data: [, , , , , , ],
		type: 'line',
	},
],

上述配置会渲染出柱形图,折线图,:

不同类型图表的配置结构有较大区别,详情请参考

Tips:

图表大致上可以分为以下几类:

上例图表依赖于坐标系组件,必须同时提供图表系列与坐标系的定义才能正常运行。对于这部分图表,坐标系相当于容器,图表的数据语义、渲染、边界都由坐标系控制。第 5 类图表则更加内聚,不依赖于其他组件。

ECharts 组件大致可分为:

Tips
部分组件不能单独使用,需要与关联组件同时配置才能生效,比如 grid + xAxis + yAxis 组合用于配置直角坐标系;polar + angleAxis + radiusAxis 组合用于配置极坐标系。

许多配置项在设计上具有多级继承的特性,是在组件、图表的配置上设定相同的同名。运行时,ECharts 会沿着配置链路逐层向下 merge,比如 textStyle ,在 title 配置项中有同名 ,在下例中:

const option = {
	textStyle: { color: '#fff', fontStyle: 'normal' },
	title: {
		textStyle: { color: '#eee' },
	},
};

最终作用在 title 上的 textStyle 值等于 merge(option.textStyle, option.title.textStyle),结果为:

title.textStyle = {
	// 值来自 option.title.textStyle.color
	color: '#eee',
	// 值来自 option.textStyle.fontStyle
	fontStyle: 'normal',
};

Tips
继承能力能够实现配置项的复用,当某些配置值能够在多个组件、图表上复用时,应该提出来在更上层作为基础配置,可有效减少冗余配置项。

在实例运行过程中可多次 setOption 接口,实现动态更新图表。接口能够对先后提供的多个 option 参数做 merge 操作,达到增量更新的,例如上面示例中,首次提供的配置项为:

const option = {
	tool@R_913_2@: { feature: { saveAsImage: {} } },
	xAxis: {
		type: 'category',
		data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
	},
	yAxis: { type: 'value' },
	series: [
		{
			type: 'line',
			data: [, , , , , , ],
		},
	],
};

myChart.setOption(option);

后续更新时,只需要提供发生变动的那一部分的配置项, setOption 会对前后配置对象做 merge 计算,例如:

const partialOption = {
	series: [{ data: [, , , , , , ] }],
};
myChart.setOption(partialOption);

// merge 后的结果:
{
	tool@R_913_2@: { feature: { saveAsImage: {} } },
	xAxis: {
		type: 'category',
		data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
	},
	yAxis: { type: 'value' },
	series: [
		{
			type: 'line',
			// 原始配置
			// data: [820, 932, 901, 934, 1290, 1330, 1320],
			// merge 后的series数组
			data: [, , , , , , ]
		},
	],
}

Tips
lazyUpdatetrue 时,图表不会立刻被更新渲染,ECharts 会等待下一帧发生时再尝试执行更新,帧的调度由 zepto 实现,底层依赖 requestAnimationFrame 接口,详情可参考 。

merge 特性实现了形式上的部分更新,ECharts 底层执行的实际上是清空图表之后重新渲染所有组件、图表 —— 即使我们只是了配置上的一小部分。这种处理模型的背后是 ECharts 将配置对象视作原子对象,每次 setOption 接口都被认为是全新的配置对象,不对之前的渲染结果做任何复用。ECharts 提供了另更佳,但受限的更新接口: ,详情请参考下一节。

echartInstance.appendData 接口用于向已有的数据序列追加更多数据项,接口后不会改变任何已渲染的组件、图表,只会在对应图表上追加数据图案,更佳。appendData 接口签名:

(opts: {
	// 要数据的系列序号。
	seriesIndex?: string,
	// 的数据。
	data?: Array | TypedArray,
}) => void;

Tips
的返回值是 string,但实测几个版本都返回 undefined,不知是不是接口与文档没有同步更新好。

基础示例:

<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		< charset="utf-8" />
		< http-equiv="X-UA-Compatible" content="IE=edge" />
		< name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: px; height: px;"></div>

		<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
		<script type="text/javascript">
			const myChart = echarts.init(document.getElementById('main'));
			let len = ;
			const random = (min = , max = ) => Math.round(Math.random() * (max - min) + min);
			const range = (from, to) => {
				const result = [];
				for (let i = from; i <= to; i++) {
					result.push(`X-${i}`);
				}
				return result;
			};

			const option = {
				// appendData 接口不会更改初始渲染后的任何组件
				// 所以需要预留足够的空间来容纳追加的图案
				xAxis: { type: 'category', data: range(, ) },
				// 同样的,在 y 轴上也需要设定好序列的最大值,以容纳追加的图案
				yAxis: { type: 'value', max:  },
				series: [
					{
						type: 'bar',
						data: [random(), random()],
					},
				],
			};
			myChart.setOption(option);

			setInterval(() => {
				myChart.appendData({ seriesIndex: , data: [random()] });
			}, );
		</script>
	</body>
</html>

示例中 setInterval 不断追加数据项,:

appendData 有很大的限制 —— 它不会改变任何已经渲染好的图形元素,比如上例在渲染追加图表项时,即使坐标轴预定的数值范围无法容纳新增的数据,ECharts 也不会对坐标轴做任何变动,因此在上述示例需要在 xAxisyAxis 配置上预留足够的空间来容纳追加的数据。

Tips
这个限制导致 appendData 接口对坐标系图表来说特别鸡肋,实用性低,甚至在官网提供的实例也很少见到 appendData 的用例。变通是混合使用 setOptionappendData,例如在直角坐标系中,用额外的变量记录当前 x、y 轴的最大最小值,如果新增的数值超出这个范围的时候就通过 setOption 更新图表;否则尽量使用 appendData

appendData 在地图散点图上表现的很好,但其他场景上限制多弱,带来的问题多过便利,所以多数情况下都会退化为使用 setOption 接口维护数据状态。

此外,appendData 还有如下限制:

Tips

setOptionappendData 外,Echarts 没有再提供其他维护数据的接口,数据的、插入、更改都没有官方推荐的,需要开发者自行处理。

ECharts 图表不具备响应式特性,初次渲染后不会因为容器尺寸的变化做自适应调节,需要自行监听屏幕尺寸的变化,并随之 ,签名:

(opts?: { width?: number | string, height?: number | string, silent?: boolean }) => ECharts;

参数:

为了实现图表元素响应屏幕尺寸的变化,通常可以加入如下片段:

window.addEventListener('resize', myChart.resize);

增述片段后,在 SPA 场景下,当图表随而析构后务必移除对应的事件监听,否则 ECharts 实例对象会一直被事件系统保留引用,导致内存泄漏!但是 ECharts 并没有暴露示例的析构事件,处理时机只能由开发者自行把握,以 vue 为例,推荐的:

Vue.component('HelloWorld', {
	mounted() {
		this._ec = echarts.init(xxx);
		window.addEventListener('resize', this._ec.resize);
	},
	beforeDestroy() {
		window.removeEventListener('resize', this._ec.resize);
	},
});


联系我
置顶