美文网首页
Echarts架构解析

Echarts架构解析

作者: 硅谷干货 | 来源:发表于2023-08-11 11:42 被阅读0次

    前言

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

    Echarts架构图

    image-20230810152537639.png

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

    • Series
    • Coordinates
    • Components

    整体构成了下述的图表:

    image-20230810152613818.png

    基石: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)
    • 辅助工具库等

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

    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如下图所示:

    • Series(MS的Excel很早对这种图表元素类型英文定义为Series,所以图表类型也沿用Excel的英文)
    image-20230810154308333.png

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

    • Coordinates(坐标系:笛卡尔坐标系、极坐标系、地理坐标系等)
    image-20230810153500746.png

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

    • Components(图标组件:标题、提示框、工具栏、图例等)
    image-20230810153531140.png

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

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

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

    image-20230810153601840.png

    举个例子,比如鼠标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(这一点跟浏览器的机制很像)。具体如下图所示:

    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组件本身状态改变时候的更新处理。

    相关文章

      网友评论

          本文标题:Echarts架构解析

          本文链接:https://www.haomeiwen.com/subject/mdrtmdtx.html