圖可視化之從零開始學G6

爲何寫這篇文章?

  • A 我想學習,我喜歡學習。
  • B 工做須要,接下來會和 G6 打交道,就是和錢包有關係。
  • C 整理下最近看的文章,用本身的文字輸出一遍,加深印象。

目標

但願讀完系列文章後,可以對 G6 的功能分佈、主要流程有大概的認知。 關注點在 G6 的介紹和主要流程,不涉及到具體 api 的使用。node

G6 是什麼?

下面各個維度解釋下G6git

G6 是一個圖可視化引擎。它提供了圖的繪製、佈局、分析、交互、動畫等圖可視化的基礎能力。旨在讓關係變得透明,簡單。讓用戶得到關係數據的 Insight。github

G6 是 antv 體系的一個圖可視化品牌,主要關注關係圖的繪製,antv 還有其餘應用,好比關注圖表的 G二、F2,關注地理數據渲染的 L7 等算法

G6 是一個開源的 JavaScript 圖形庫,能夠支持 PC、移動端、小程序多個平臺。npm

更多G6使用的示例參見圖可視化引擎 G6json

G6 的上下游

G6 依賴渲染引擎 G 提供渲染能力,G 是一款Antv體系開源的支持 canvas、svg 渲染,跨 PC、mobile 平臺的強大渲染引擎。 Graphin是基於 G6 的 React 庫,經過接入 React 生態,使用起來更加簡單。bootstrap

功能分佈

上圖是官方 v4 的框架圖,比較能看出 G6 基礎的功能分佈。canvas

  • 跨 PC、mobile 平臺
  • 核心功能:狀態管理、事件及交互、動畫、佈局;
  • 擴展能力:插件、自定義基礎類型
  • 底層的 GPU 加速計算、g-base 的渲染能力等;

工程結構

G6 是一個多項目共存的庫,使用monorepo的思想來管理項目。monorepo不作過多介紹。思路就是把相關聯的項目都放在一個倉庫裏管理,包括開發、構建、發佈等。我本身經驗來看,這種模式確實在同時多個項目開發時有優點,免去管理、link 多個項目的麻煩。不過上手階段我常常會有疑惑,不能確認問題出在哪一個項目,要總體build,苦笑,應該是我太菜。G6 使用 lerna 來管理全部的子項目,目錄結構和簡介以下:小程序

G6
|-packages
  |--core // G6核心庫,實現了大部分功能,主流程、動畫、交互、狀態管理
  |--pc // 擴展core,提供更多的交互,pc的事件等
  |--mobile // 擴展core,提供mobile、小程序平臺的支持,移動端事件支持等
  |--element // 提供預製的自定義圖元素(節點、邊、combo)
  |--plugin // 插件,好比[魚眼效果](https://g6.antv.vision/zh/examples/tool/fisheye#fisheye)
  |--...
複製代碼

構建項目:npm install , npm run bootstrap ,更多指令能夠看package.json文件api

渲染引擎G

功能

提供渲染能力,向上提供

主流程

  1. 頂層抽象流程是輸入節點樹,遍歷並渲染兩個步驟。
  2. 深度遍歷,遍歷的順序就是節點的繪製順序,能夠用來控制 z-index。
  3. G的渲染循環觸發是通知觸發的,不是每幀渲染。初次進入、有事件或動畫的時候觸發渲染,每次收集一波更新後在下一次 requestFrame 回調時繪製,這樣處理應該能節省很多靜止時繪製消耗。動畫則使用額外的依賴 d3-timer 來驅動。
  4. 遊戲引擎裏會在節點樹和繪製接口draw之間增長渲染隊列,方便對渲染隊列作合批優化。

概念

  • group:節點分組,組織子節點,構建樹結構。
  • shape:子節點,承載具體的繪圖能力和渲染數據,g-base 提供了圓、矩形、線、多邊形等基礎能力。
  • 詳細的數據結構官方文檔有找到shape的,參見文檔。數據結構描述了類型的形狀和狀態,數據流和狀態轉變能跟蹤整個系統的運轉,對理解和把控系統頗有幫助。各類類型的shape有一些共同的樣式結構,好比fill、shadowColor。各自又有一些特殊屬性,好比圓形的半徑、矩形的寬高。

小結

本小節簡單描述了G的流程和輸入的數據結構,用來輔助理解G6。一些更細的點,好比局部渲染、形狀拾取、碰撞檢測、座標系統等,就暫時無論。從流程圖能夠看出,G 跑起來須要 Group 樹結構,因此接下來 G6 須要作的工做就是構建這樣的結構,交給 G 來渲染。

G6

前置文檔說明

G6的核心概念 在官方文檔有講,此處再也不贅述,能夠先創建下概念和基礎用法。一個簡單的示例以下:

const data = {
  nodes: [
    {
      id: "node1",
      label: "Circle1",
      x: 150,
      y: 150
    },
    {
      id: "node2",
      label: "Circle2",
      x: 400,
      y: 150
    }
  ],
  edges: [
    {
      source: "node1",
      target: "node2"
    }
  ]
};

const graph = new G6.Graph({
  container: "container",
  width: 500,
  height: 500,
});

graph.data(data);
graph.render();

複製代碼

例子簡單,但也能基本說明問題。能夠看出:

  • 使用流程:就是準備數據和配置,建立Graph實例,傳入數據,而後render
  • 數據結構:G6使用節點和邊兩個類型描述整個圖,節點就是圖的節點,邊描述節點之間的關係。節點:邊是1:n的關係。

在此基礎上有交互(behavior)、事件、狀態管理、動畫、佈局幾大功能。下面就按照G6的執行順序,分析下G6的基礎流程和相關設計。

graph實例

建立graph實例後,同時會初始化如下單例,管理不一樣的功能:

  • graph.cfg.canvas

    G 對應的 Canvas 類實例,調控整個渲染流程,經過該單例與 G 對接起來

  • graph.cfg.itemController

    管理 Item 實例,Item 是 G6 包裝的節點類,輸入的邊和節點數據會組織成 item 實例數組。這個模塊打通從數據到最終渲染的整個流程,下面的章節會着重分析這個模塊相關的部分。

  • graph.cfg.layoutController

    控制佈局相關邏輯,佈局算法比較複雜,G6 拆出了一個 npm 包(@antv/layout)專門放佈局相關算法。

  • graph.cfg.viewController

    控制顯示相關邏輯。核心邏輯是計算視口居中,提供座標轉換等功能。

  • graph.cfg.eventController

    控制事件相關邏輯,核心邏輯是拾取 G 傳來的事件,出發用戶定義事件,最終反饋到 item 上。

  • graph.cfg.modeController

    控制交互,管理 behavior。

  • ShapeFactory

    圖元素的工廠函數,管理(crud、函數調用)定義的節點、邊、combo。

繪製流程

下圖展現了一個節點數據(參照前置文檔示例中節點的數據結構)從輸入,到最終渲染,數據的流動狀態,以及經歷的流程。

  1. 建立 graph 實例,建立 G 的 Canvas 模塊實例,輸入節點數據。
  2. 調用 graph.itemController.addItem, 使用data 建立出 Item 實例並保存在 itemController 中。
  3. 建立出的 item 使用 factory 找到對應繪製節點,包括自定義節點shapeA。
  4. shapeA.drawShape 將 shape 添加到 group 中,構建出渲染樹。
  5. G 繪製 Group 樹。

節點的組織

data輸入後,須要依靠itemController建立出Item實例來管理。Item做爲G6與渲染層的橋接,能夠仔細分析下。Item類設計以下:

Item

  • Item 關注G6這一層的功能的支撐,圖元素的組織、狀態等。
  • Item 分爲三個子類,Node,Edge,Combo 管理不一樣的行爲。好比Node會有鏈接點,Edge會有箭頭等,在子類中定義。狀態、繪製流程相關公用的邏輯就放Item類。
  • Item 的子類和ShapeFactory的子factory是n:1的關係(參見Item的init方法)。好比Node類,會對應到NodeFactory。若是擴展新的ShapeFactory,須要考慮Item子類的聯動。

ShapeFactory

  • 目前默認有三個子factory,NodeFactory、EdgeFactory、ComboFactory,分別對應三種圖元素類型。
  • 職責是管理(新增,查找)圖元素,能夠理解是圖元素工具類的集合。
  • 使用抽象工廠模式,能夠新增子factory或在子factory增長新的類型,擴展性很強。

圖元素

  • 圖元素類的關注的是 G 的shape,給 G6 支持到繪製能力。自身不保存任何狀態,屬於工具類。
  • 圖元素類與shape的關係是1:n,能夠經過組合不一樣的shape新增圖元素類型。
  • 圖元素官方定義三種基礎類型,節點、邊、combo,介紹和數據結構詳見文檔
  • 圖元素類中的固定接口,會在程序運行時調用。好比drawShape,Item在添加的時候,會調用該函數,給渲染層增長shape。

查找圖元素流程

  • 一個具體的Item實例,好比Node 實例A;
  • 輸入 A.type 給 ShapeFactory 找到 NodeFactory;
  • 輸入 A.cfg.type 給 NodeFactory就能夠找到註冊的圖元素類;
  • 使用圖元素繪製或執行動畫等

其餘功能

前面裏串通了繪製流程,已經能畫出了圖。除此以外,做爲圖可視化引擎的G6,還支持了哪些功能,又怎麼實現的呢?

  • 事件、交互、狀態,這個不用多說,只看圖不能交互,味道是差了些
  • 動畫,加點緩衝效果,視覺更好了
  • 佈局,內置了豐富的佈局樣式以匹配各類場景。
  • ... ...

本文總體的思路是,分析這些功能互相的關係、內部的一些流程、設計 、實現點,這樣基本能從大到小,解釋清楚整個系統是怎麼運行的。

總體關係

各個功能的分層關係以下:

整個G6就這些核心功能,從圖中能夠看出各個功能之間的層次關係,能瞭解到功能的所處位置。有了大方向上的認知後,能減小些迷茫。

這些功能除了動畫,均有對應的Controller,由Graph封裝並放在Graph.cfg上,並對外提供入口。G6有個設計的特色,就是全部狀態相關的都會放在cfg裏,表明的應該是這個類型的model。好比Graph類中的cfg,Item類中的Cfg等。不過把Controller這種邏輯密集的也掛在上面總感受有些奇怪。

事件、動畫、Item節點管理會直接依賴G。而交互、狀態管理、佈局則更多基於G6本身的封裝。

事件

官方文檔對事件有清晰的分類。總共分爲三類事件:畫布層次的事件(canvas:mousedown ...),節點層次的事件(node:click ...),按時機的事件(afterlayout ...)。事件包括事件名,以及事件的回調。事件名在不一樣平臺會有差別,好比pc平臺的mouse事件,移動平臺的touch事件。詳細介紹見文檔中核心概念章節的事件小節。

事件咱們都比較熟悉,觀察者、發佈訂閱之類。G6事件也不例外,有個全局的事件中心,graph.cfg.eventController,提供事件的訂閱和發佈。而與咱們認識的Dom的事件不一樣的是,G6面對的可能只有一個Dom元素,Canvas,因此不能依賴平臺自己解析事件觸發的節點、事件對象,須要本身實現及封裝。

  • 按時機的事件使用eventController作發佈訂閱就能夠了。
  • 畫布、節點層次的事件,G和G6都有部分實現。G的樹節點繼承了@antv/EventEmiter,讓每一個節點支持事件註冊及冒泡,相似dom。G6接管了G的樹節點根元素的全部事件,並統一派發。從這個實現思路看,其實shape是能夠本身註冊事件的(G裏樹節點支持on/off),G6的文檔則只提到經過Graph註冊事件,應該是認爲G更加底層,使用時不該該接觸這一層的行爲。
  • 以click爲例說明下事件的流程:
    1. G6建立item時,給對應的group掛上本身的引用
    2. 用戶觸發click
    3. 觸發畫布原生事件
    4. G根據點擊座標作形狀拾取事件冒泡
    5. G6觀察到根部元素事件觸發,並拿到傳遞來的shape,進一步就能夠作事件的派發、驅動交互功能

形狀拾取

經過上篇文章,咱們知道,G將繪圖的節點組織成group樹結構,shape是樹的子節點。要肯定到底點擊到哪一個shape/group,思路其實顯而易見就是遍歷這棵樹,找到節點便可。下面是quickHit模式下的節點拾取流程,源碼參見G項目中g-base包裏event.ts文件。(quickHit模式經看源碼,主要是忽略了group的點擊檢測,只檢測葉子節點,經過這樣處理提高性能。)

  1. 觸發點擊
  2. 獲取到源事件對象
  3. 獲取到點擊點座標
  4. 從右向左開始深度遍歷Group樹(渲染的時候是從左到右,最後渲染的在上面,最早觸發事件)
  5. 根據點擊座標過濾(跳過隱藏的、禁止拾取、畫布外的樹節點)
  6. 點擊檢測到座標點在形狀內,判斷找到形狀,返回形狀
  7. 遍歷完成未命中則斷定未觸發

其中點擊檢測每一個shape類型有實現本身的檢測方法,好比圓使用點到圓心的距離等。每次觸發事件都要遍歷整顆樹,作點擊檢測,是個性能點,所以G自己也作了一些緩存處理。

事件冒泡

事件冒泡是G作的,比較簡單,核心思路是檢測命中shape後,依次向上觸發parent的事件,直到事件對象裏的 propagationStopped 屬性爲true或者到達根元素。  

交互模式

**交互模式解決的問題?**是用來批量操做用戶交互行爲,好比切換編輯和查看模式這種場景。詳細介紹見文檔中核心概念章節的Behavior相關小節。 類設計:

Behavior是個工廠類,使用對像存儲各個Behavior類型。每一種Behavior子類型,定義了所需的事件行爲集合,提供事件的綁定與解綁。 ModeController,根據輸入的mode配置(Behavior類型的索引組成的數組),或動態切換不一樣的Behavior子類實例。

ModeControler綁定Behavior流程:

核心思路就是ModeController調用Behavior工廠建立子類型實例,實例完成綁定過程。觸發時機是初始化Graph或系統運行期間動態調用Graph.setMode。

狀態管理

狀態管理解決的問題? 實現交互時或者業務邏輯中節點狀態變化後,觸發節點樣式的更新或其餘自定義行爲。這裏的狀態能夠是交互中的hover、active,也能夠是自定義的running任何一種狀態。對狀態的響應若是隻是樣式變化,能夠直接在建立Graph實例時輸入的節點配置中設置。若是其餘更加複雜的行爲,好比加個動畫,就須要自定義節點,複寫默認的setState方法。詳細介紹見文檔核心概念章節的交互mode、狀態state小節

類設計:

Graph初始化後會實例化StateController和ItemController,能夠在Graph實例的cfg中訪問到。

StateController目前看來只是維護了各類狀態下的Item數組,以及更新狀態後的事件觸發,不影響整個state的流程。

設置狀態流程

  • 輸入數據中配置不一樣狀態的樣式
  • 事件觸發或流程觸發,調用graph.setItemState更新狀態
  • 查找配置中狀態對應的樣式
  • item(須要更新的節點)定位到具體的shape,調用shape的setState,更新樣式到G渲染層

動畫

動畫G6沒有作什麼處理,直接使用的G的動畫能力。根據使用場景,分爲全局動畫自定義節點動畫兩部分。詳細介紹見文檔核心概念章節的基礎動畫小節。 **全局動畫:**拿到節點樹根部元素,調用animate接口;

自定義節點動畫:在自定義圖元素(能夠看上篇瞭解圖元素,G6用來對接G,封裝了具體的shape)時,拿到shape或group節點,調用animate接口便可。

類設計:

  • Element實現了animate接口,子類都具備了動畫能力。
  • Animation這個類源碼裏沒有單獨的文件體現,不過有用ts聲明區分出來,記錄了動畫的詳細配置。
  • Canvas類是G這一層的主控類和入口。根節點、動畫入口、繪製入口等,都在這個類上。
  • Timeline類使用Element和Animation實現具體的動畫算法。使用的時候做爲單例掛在Canvas實例上。

動畫流程:

核心思路就是Element獲取Canvas中的TimeLine實例,傳入動畫數據,交給d3-timer去逐幀執行,經過插值實現連續的動畫效果。

插值計算使用了d3-ease、d3-interpolate輔助計算。

更新到畫布是Element的能力。

觸發流程時機是全局動畫或自定義動畫調用animate時。

佈局

佈局是採用算法,使節點和邊可以以某種方式分佈。分爲通常圖佈局和樹圖佈局,目前我只看到了通常圖佈局,就只分析通常圖佈局。詳細介紹見文檔核心概念章節的圖佈局小節。

相關的類:

佈局使用在初始化Graph時建立的LayoutController控制。具體的佈局算法來自npm包** @antv/layout** 。

通常圖佈局的具體流程

能夠看出,佈局和Graph經過數據解耦。Graph面向數據,數據正確就能正常渲染。佈局算法也只用關注對數據的生產,不用關注Graph或者其餘系統。

佈局算法對節點座標更新以達到佈局的目的,其餘數據項也能夠更新,不過就顯得職責不清晰,因此不建議佈局算法對其餘數據項的更新。

PC和Mobile平臺佈局的流程不一致。PC會額外支持worker佈局,就是圖中第三列的佈局流程,把佈局拆成了三個步驟異步通知進行。

觸發流程的時機是在初始化或者切換數據、佈局的時候,具體是在調用Graph的render/changeData/updateLayout時。

這裏是一個自定義佈局的例子,看完這個例子應該會對佈局算法的職責有更清晰的認知。

小結

經過對各個功能的層次分佈,以及各自實現流程的分析,應該對G6實現思路有了大體的認知。有興趣的話,能夠對着流程看看源碼,看看具體某個步驟的實現邏輯。通過這段時間G6的梳理,對G6也有了更多的瞭解,最後感謝閱讀。

最後

微信搜索公衆號Eval Studio,關注更多動態。

相關文章
相關標籤/搜索