Echarts架构解析

2023-08-11  本文已影响0人  硅谷干货

前言

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

Echarts架构图

image-20230810152537639.png

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

整体构成了下述的图表:

image-20230810152613818.png

基石:ZRender

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

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

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

image-20230810152658797.png

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

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();

两者效果如下:

image-20230810152719582.png

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

image-20230810152740132.png

这里面又有文字 + 扇形 + 线段等元素的图形,如果直接用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渲染就可以了。大致流程如下图:

image-20230810152834795.png

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

image-20230810152930189.png

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

image-20230810153016966.png

上述为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):

image-20230810153108958.png

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

image-20230810153139479.png

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

image-20230810153159583.png

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

image-20230810153228194.png

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

image-20230810153303158.png

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

image-20230810153327195.png

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

image-20230810154308333.png

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

image-20230810153500746.png

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

image-20230810153531140.png

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

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

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

image-20230810153601840.png

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

Final Option = User Option + UserData + UI State。

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

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

image-20230810153633325.png

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

image-20230810153755905.png

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

Series中一般命名习惯为:

image-20230810153831992.png

Component中一般命名习惯为:

image-20230810153906544.png

以及其中每个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

image-20230810153952581.png

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

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

image-20230810154035323.png

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

image-20230810154121391.png

官方文档

image-20230810161820821.png

整体小结

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

上一篇下一篇

猜你喜欢

热点阅读