前言
本文章主要介紹ECharts整體的架構(gòu)設計,以及源碼中關鍵的代碼部分夏漱,用于簡單對ECharts的設計以及工作概念有個簡單的入門理解豪诲,所以不會講到太深入源碼地方,幫助想了解ECharts的同學入門挂绰。
Echarts架構(gòu)圖
Echart底層依賴矢量圖形庫ZRender屎篱,基于ZRender之上做了更高層次的抽象,定義出了以下三種元素:
- Series
- Coordinates
- Components
整體構(gòu)成了下述的圖表:
基石:ZRender
GitHub - ecomfe/zrender: A lightweight graphic library providing 2d draw for Apache ECharts
ZRender是二維繪圖引擎葵蒂,它提供Canvas交播、SVG、VML 等多種渲染方式践付∏厥浚基于這些之上定義了如下幾個概念:
- animation 定義動畫
- graphic 定義圖形
- mvc 定義模式
- 統(tǒng)一UI Event(封裝多平臺操作差異)
- 多種渲染引擎(Canvas/SVG/WebGL)
- 輔助工具庫等
其中最核心的是定義圖形:
于是乎,你在你就可以通過簡單的幾行代碼畫圓形了
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();
兩者效果如下:
整體來說沒太大的差別,但是你會發(fā)現(xiàn)如果不用ZRender的抽象的話隧土,你會寫一堆復雜更底層的canvas的api代碼提针。假如demo是如下的餅圖:
這里面又有文字 + 扇形 + 線段等元素的圖形,如果直接用canvas次洼,那代碼量將會是爆表关贵。所以ZRender基于Canvas、SVG卖毁、WebGL這些渲染方式揖曾,自己進行了底層代碼封裝。你只知道ZRender的基本圖形以及API相關的用法亥啦,基本也能夠滿足日常圖形繪制等等相關的需求了炭剪。當然,具體代碼可以通過Github這里查看翔脱,這里拿Line作為舉例(https://github.com/ecomfe/zrender/blob/master/src/graphic/shape/Line.ts)
所以奴拦,對于EChart來說,基本底座也是基于ZRender届吁,他只需要根據(jù)實際用戶傳入的Option進行Create错妖、Update,然后解析出每個圖標元素所需要的圖形疚沐,再交由ZRender渲染就可以了暂氯。大致流程如下圖:
此外,ZRender還提供了統(tǒng)一的UI Event管理用于屏蔽底層差異
ECharts高級的抽象:序列 & 組件 & 坐標系
上述為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) 兩個核心接口,具體職責是負責調(diào)用ZRender繪制展示以及處理用戶交互究流。
Series用于描述數(shù)據(jù)的表現(xiàn)形態(tài)辣吃,如:折線、餅圖芬探、柱狀圖等
具體源碼對應位置如下(https://github.com/apache/echarts):
Components用于豐富圖表中的具體展現(xiàn)和交互形式:XYAxis神得、Legend、Title偷仿、Toolbox等
當然除了Series和Component并不能去構(gòu)成一個圖表哩簿,還缺少一個具體的坐標系系統(tǒng)。Coordinates其核心的主要是把Series轉(zhuǎn)化成坐標系的點的位置炎疆,以及協(xié)同一系列的組件完成一個圖表坐標的功能卡骂。具體坐標系在ECharts源碼中如圖所示:
所有的坐標系都實現(xiàn)了CoordinateSystem接口定義(https://github.com/apache/echarts/blob/master/src/coord/CoordinateSystem.ts)其中最核心的代碼為:
定義了具體的Series的data部分數(shù)據(jù)怎么轉(zhuǎn)化為坐標系中的點国裳。舉個簡單的代碼例子(以下以Gird坐標系為例):
然后當Model發(fā)生變化形入,觸發(fā)UI重新Layout的時候,這里則會調(diào)用掉具體的坐標系系統(tǒng)進行Series的data重新生成為點的位置缝左。具體代碼位置如下(https://github.com/apache/echarts/blob/master/src/layout/points.ts):
具體Echart目前提供的Series&Components&Coordinates如下圖所示:
- Series(MS的Excel很早對這種圖表元素類型英文定義為Series亿遂,所以圖表類型也沿用Excel的英文)
其實這里的Series并不包含坐標系的概念浓若,你可以理解是數(shù)據(jù)的表達方式的結(jié)構(gòu)
- Coordinates(坐標系:笛卡爾坐標系、極坐標系蛇数、地理坐標系等)
這里的坐標系是為了把Series中的DataList轉(zhuǎn)化為坐標系上的點
- Components(圖標組件:標題挪钓、提示框、工具欄耳舅、圖例等)
這里的Components主要是為了輔助圖表用戶某些功能的組件
狀態(tài)&事件:狀態(tài)管理工作流與事件處理
當用戶通過鼠標去點擊圖標中的某個元素碌上,就會有相對應的互動。
舉個例子浦徊,比如鼠標Hover到圖中的某類數(shù)據(jù)的時候馏予,其他類別的圖表需要進行淡化處理。這里可跟用戶輸入的Option不一樣盔性,每個組件其實內(nèi)部都是有自己的UI相關的狀態(tài)霞丧,所以這里圖標淡化或者是餅圖扇區(qū)變大這種UI狀態(tài)相關的操作都是由組件本身的State去控制的,最終也是通過ZRender去渲染冕香。這里有個簡單公式:
Final Option = User Option + UserData + UI State蛹尝。
其中只要任何一個項變化,都會觸發(fā)整個EChart實例進行渲染悉尾,其中:
- UserOption可以通過 setOption去改變
- UserData可以因為Legend點擊隱藏某一項data而改變突那,也可以因為setOption改變
- UI State可以因為用戶進行某種UI操作改變了UI狀態(tài),比如餅圖彈出焕襟、Hover高亮折線圖等
上述所有的行為陨收,都會觸發(fā)UI的重新Layout和Render(這一點跟瀏覽器的機制很像)。具體如下圖所示:
所以根據(jù)上述這種UI更新的機制鸵赖,EChart拆分出了Model和View兩個概念务漩,通過Model數(shù)據(jù)改變進而去驅(qū)動View展示。整體數(shù)據(jù)派發(fā)就是簡單且單一的數(shù)據(jù)流它褪。
在源碼設計中饵骨,一般我們可以看到如下的命名規(guī)范:
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)
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)
以及其中每個EChart實例對應一個EcModel負責管理所有的Model狀態(tài)分發(fā),具體GlobalModel邏輯可以從源碼去了解(https://github.com/apache/echarts/blob/f3471f0a70/src/model/Global.ts)茫打。
每當用戶點擊圖標中的Series或者是Component的時候居触,都會觸發(fā)ZRender的UI事件,通過UI事件Dispatch給GlobalModel老赤,然后GlobalModel則會通知需要更新的所有關聯(lián)的組件(當然這里每個組件都做了對應的ZRender事件派發(fā)監(jiān)聽轮洋,如下代碼所示,以Pie這個組件為例)抬旺。
https://github.com/apache/echarts/blob/f3471f0a70/src/chart/pie/install.ts
此處由統(tǒng)一的Action進行托管所有ZRender的事件類型弊予,動態(tài)生成監(jiān)聽函數(shù)。
https://github.com/apache/echarts/blob/f3471f0a70/src/legacy/dataSelectAction.ts
這里上一張完整的EChart數(shù)據(jù)流圖:
官方文檔
整體小結(jié)
EChart的底層是基于ZRender的繪圖能力开财。并且在這個基礎之上汉柒,抽象了系列误褪、組件、坐標系三種基礎概念碾褂。并且在這個概念之上兽间,設計出了一套單向的數(shù)據(jù)流,用于處理用戶數(shù)據(jù)輸入以及UI組件本身狀態(tài)改變時候的更新處理正塌。