当前位置: 首页>后端>正文

Echarts架构解析

前言

本文章主要介绍ECharts整体的架构设计,以及源码中关键的代码部分,用于简单对ECharts的设计以及工作概念有个简单的入门理解,所以不会讲到太深入源码地方,帮助想了解ECharts的同学入门。

Echarts架构图

Echarts架构解析,第1张

Echart底层依赖矢量图形库ZRender,基于ZRender之上做了更高层次的抽象,定义出了以下三种元素:

  • Series
  • Coordinates
  • Components

整体构成了下述的图表:

Echarts架构解析,第2张

基石:ZRender

GitHub - ecomfe/zrender: A lightweight graphic library providing 2d draw for Apache ECharts

ZRender是二维绘图引擎,它提供Canvas、SVG、VML 等多种渲染方式。基于这些之上定义了如下几个概念:

  • animation 定义动画
  • graphic 定义图形
  • mvc 定义模式
  • 统一UI Event(封装多平台操作差异)
  • 多种渲染引擎(Canvas/SVG/WebGL)
  • 辅助工具库等

其中最核心的是定义图形:

Echarts架构解析,第3张

于是乎,你在你就可以通过简单的几行代码画圆形了

var zr = zrender.init(document.getElementById('main'));
var circle = new zrender.Circle({
    shape: {
        cx: 150,
        cy: 150,
        r: 40
    },
    style: {
        fill: 'none',
        stroke: '#F00'
    }
});
zr.add(circle);

当然这里只是画一个圆,如果用canvas代码则如下:

var canvas = document.getElementById('main1');
canvas.width = 300;
canvas.height = 300;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = '#F00';
ctx.arc(150, 150, 40, 0, Math.PI * 2);
ctx.closePath();
ctx.stroke();

两者效果如下:

Echarts架构解析,第4张

整体来说没太大的差别,但是你会发现如果不用ZRender的抽象的话,你会写一堆复杂更底层的canvas的api代码。假如demo是如下的饼图:

Echarts架构解析,第5张

这里面又有文字 + 扇形 + 线段等元素的图形,如果直接用canvas,那代码量将会是爆表。所以ZRender基于Canvas、SVG、WebGL这些渲染方式,自己进行了底层代码封装。你只知道ZRender的基本图形以及API相关的用法,基本也能够满足日常图形绘制等等相关的需求了。当然,具体代码可以通过Github这里查看,这里拿Line作为举例(https://github.com/ecomfe/zrender/blob/master/src/graphic/shape/Line.ts)

所以,对于EChart来说,基本底座也是基于ZRender,他只需要根据实际用户传入的Option进行Create、Update,然后解析出每个图标元素所需要的图形,再交由ZRender渲染就可以了。大致流程如下图:

Echarts架构解析,第6张

此外,ZRender还提供了统一的UI Event管理用于屏蔽底层差异

Echarts架构解析,第7张

ECharts高级的抽象:序列 & 组件 & 坐标系

Echarts架构解析,第8张

上述为EChart中具体的UI元素,主要分为两种类型:Series和Components。它们的都继承于View目录中的Chart.ts(https://github.com/apache/echarts/blob/f3471f0a70/src/view/Chart.ts) 以及Component.ts(https://github.com/apache/echarts/blob/f3471f0a70/src/view/Component.ts) 两个核心接口,具体职责是负责调用ZRender绘制展示以及处理用户交互。

Series用于描述数据的表现形态,如:折线、饼图、柱状图等

具体源码对应位置如下(https://github.com/apache/echarts):

Echarts架构解析,第9张

Components用于丰富图表中的具体展现和交互形式:XYAxis、Legend、Title、Toolbox等

Echarts架构解析,第10张

当然除了Series和Component并不能去构成一个图表,还缺少一个具体的坐标系系统。Coordinates其核心的主要是把Series转化成坐标系的点的位置,以及协同一系列的组件完成一个图表坐标的功能。具体坐标系在ECharts源码中如图所示:

Echarts架构解析,第11张

所有的坐标系都实现了CoordinateSystem接口定义(https://github.com/apache/echarts/blob/master/src/coord/CoordinateSystem.ts)其中最核心的代码为:

Echarts架构解析,第12张

定义了具体的Series的data部分数据怎么转化为坐标系中的点。举个简单的代码例子(以下以Gird坐标系为例):

Echarts架构解析,第13张

然后当Model发生变化,触发UI重新Layout的时候,这里则会调用掉具体的坐标系系统进行Series的data重新生成为点的位置。具体代码位置如下(https://github.com/apache/echarts/blob/master/src/layout/points.ts):

Echarts架构解析,第14张

具体Echart目前提供的Series&Components&Coordinates如下图所示:

  • Series(MS的Excel很早对这种图表元素类型英文定义为Series,所以图表类型也沿用Excel的英文)
Echarts架构解析,第15张

其实这里的Series并不包含坐标系的概念,你可以理解是数据的表达方式的结构

  • Coordinates(坐标系:笛卡尔坐标系、极坐标系、地理坐标系等)
Echarts架构解析,第16张

这里的坐标系是为了把Series中的DataList转化为坐标系上的点

  • Components(图标组件:标题、提示框、工具栏、图例等)
Echarts架构解析,第17张

这里的Components主要是为了辅助图表用户某些功能的组件

状态&事件:状态管理工作流与事件处理

当用户通过鼠标去点击图标中的某个元素,就会有相对应的互动。

Echarts架构解析,第18张

举个例子,比如鼠标Hover到图中的某类数据的时候,其他类别的图表需要进行淡化处理。这里可跟用户输入的Option不一样,每个组件其实内部都是有自己的UI相关的状态,所以这里图标淡化或者是饼图扇区变大这种UI状态相关的操作都是由组件本身的State去控制的,最终也是通过ZRender去渲染。这里有个简单公式:

Final Option = User Option + UserData + UI State。

其中只要任何一个项变化,都会触发整个EChart实例进行渲染,其中:

  • UserOption可以通过 setOption去改变
  • UserData可以因为Legend点击隐藏某一项data而改变,也可以因为setOption改变
  • UI State可以因为用户进行某种UI操作改变了UI状态,比如饼图弹出、Hover高亮折线图等

上述所有的行为,都会触发UI的重新Layout和Render(这一点跟浏览器的机制很像)。具体如下图所示:

Echarts架构解析,第19张

所以根据上述这种UI更新的机制,EChart拆分出了Model和View两个概念,通过Model数据改变进而去驱动View展示。整体数据派发就是简单且单一的数据流。

Echarts架构解析,第20张

在源码设计中,一般我们可以看到如下的命名规范:

Series中一般命名习惯为:

  • XXXSerise.ts(https://github.com/apache/echarts/blob/f3471f0a70/src/chart/pie/PieSeries.ts)
  • XXXView.ts(https://github.com/apache/echarts/blob/f3471f0a70/src/chart/pie/PieView.ts)
Echarts架构解析,第21张

Component中一般命名习惯为:

  • XXXModel.ts(https://github.com/apache/echarts/blob/f3471f0a70/src/component/legend/LegendModel.ts)
  • XXXView.ts(https://github.com/apache/echarts/blob/f3471f0a70/src/component/legend/LegendView.ts)
Echarts架构解析,第22张

以及其中每个EChart实例对应一个EcModel负责管理所有的Model状态分发,具体GlobalModel逻辑可以从源码去了解(https://github.com/apache/echarts/blob/f3471f0a70/src/model/Global.ts)。

每当用户点击图标中的Series或者是Component的时候,都会触发ZRender的UI事件,通过UI事件Dispatch给GlobalModel,然后GlobalModel则会通知需要更新的所有关联的组件(当然这里每个组件都做了对应的ZRender事件派发监听,如下代码所示,以Pie这个组件为例)。

https://github.com/apache/echarts/blob/f3471f0a70/src/chart/pie/install.ts

Echarts架构解析,第23张

此处由统一的Action进行托管所有ZRender的事件类型,动态生成监听函数。

https://github.com/apache/echarts/blob/f3471f0a70/src/legacy/dataSelectAction.ts

Echarts架构解析,第24张

这里上一张完整的EChart数据流图:

Echarts架构解析,第25张

官方文档

Echarts架构解析,第26张

整体小结

EChart的底层是基于ZRender的绘图能力。并且在这个基础之上,抽象了系列、组件、坐标系三种基础概念。并且在这个概念之上,设计出了一套单向的数据流,用于处理用户数据输入以及UI组件本身状态改变时候的更新处理。


https://www.xamrdz.com/backend/37y1936549.html

相关文章: