Cytoscape.js - 可畫網路拓撲圖(節點連線)的Javascript函式庫

限量最近接到要做Monitor即時監控機器是否連線的任務,關於這種Client Server的情況就想到用網路拓譜圖的方式顯示,然後限量就是網路上到處找適合的Javascript Library來輔助,就找到這套Cytoscape.js,接著這篇就來談談如何用Cytoscape.js來畫出網路網路拓譜圖。


Cytoscape是一個專門處理網路拓譜圖, 化學分子結構圖, 生物學圖形...等視覺化模型的軟體平台,發展至今已有好幾年的歷史,到目前為止已經支援許多平台與各種語言的函示庫或Web Service,甚至也有功能完整的圖形APP工具可以在官網的Cytoscape App Store找到。今天要談的是用於Web上的Cytoscape.js,Cytoscape.js是特別為了HTML5所獨立出來的子專案。

Cytoscape的Layout主要有幾個Component組成,Layout, Node, Edge。在詳細說明之前先來看看Cytoscape.js的簡單執行結果:



Layout

Cytoscape的Layout是最重要的部分,沒有Layout怎麼顯示圖形。要產生Layout的方式就是宣告一個HTML div,載入之後再到Javascript區塊中取得此div進行Cytoscape的初始化。初始化後可以發現div中出現了許多Canvas,因為Cytoscape繪圖基本上是用Canvas去畫的,所有Node, Edge都是Canvas畫出來的,並不屬於HTML元件,所以按F12是找不到的。
初始化方式如下:

$(function() {
    var cy = {
        container: document.getElementById(...), // 要來當作Canvas容器的div
        style: [
            selector: ...,
            css: {...}
        ], // JSON格式設定CSS Style
        ready: function() {...}, // 當Cytoscape.js初始化完成後進行的callback動作
        elements: {
            nodes: [
                {data: {...}},
                ...
            ],
            edges: [
                {data: {...}},
                ...
            ]
        }, // 加入Canvas內要畫出的Node與Edge
        layout: {
            name: ...,
        } // 設定Layout格式
    }
}


初始化時,container與layout, elements是很重要的,container指定在HTML中哪個元件要來當作Cytoscape的容器,elements設定初始化時要產生的Node與Edge,而Layout則是指定此Cytoscape的Layout是哪種樣式。
Cytosape提供的Layout有好幾種,下面限量簡單說明幾種Layout:
random:節點位置隨機分布。
circle:節點會以繞圈方式排列。
breadthfirst:節點會以階層方式排列(適用於Compound Node)。
grid:節點分布會以Grig方式排列。


Node

Node是網路拓譜圖中重要的元件之一,通常代表一個獨立角色,例如在表示多台機器的關係圖中,Node就代表一台機器,角色則可能是Server, Client, Router...等。在Cytoscape中一個Node含有幾個屬性:
{
    gruop: 'edges',
    data: {
        id: // Node ID,
        parent: // Parent ID (此Parent為Compound Node)
    }
}

Node中包含其他Sub-Node稱為Compound Node,只要該Node成為Compound Node,則就不能和其他Node有Edge,。

Edge

Edge為Node與Node彼此連接起來的線,表示角色之間的關聯性,例如在人際關係的網路拓譜圖中,如果A與B有關係就會用Edge連接起來。Edge還有以下屬性:

{
    gruop: 'edges',
    data: {
        id: // Edge ID,
        source: // source node ID,
        target: // target node ID
    }
}

看完Node與Edge的物件屬性可以發現其他兩者看起來很像,因為兩個都是繼承Element的Base Class,所以Node與Edge還有其他繼承Element的屬性可以設定:
{
    group: , // Element類型(Node or Edge)
    data: {} , // 同Node or Edge
    position: { x: , y: }, // Element位置
    grabbable: , // 設定是否使用者能夠拖拉此Element
    classes: 'class1 class2 ...', // 此Element的style class
    css: {...} // 設定此Element的CSS Style
    ...
}




在初始化後,會取到一個Cytoscape的物件,接著就可以使用此物件來對Cytoscape進行操作,Cytoscape提供了許多核心功能API,藉由呼叫這些API可以進行像對Element CRUD操作, 動畫, 重置...等,接下來下面說明核心所提供的功能與事件。 

API

核心針對不同對象有提供不同的API功能,對象有整體(Cytoscape物件(cy)), 集合(Element Collection),因為實在太多了,所以限量也就只列出幾個較常用的API:
註:zoom為放大縮小的層級(使用者可用滑鼠滾輪放大縮小圖層);pan為Layout移動的位置(Cytoscape.js的Layout如果沒有設定fit為true時,Layout可以無限延申)。


核心功能

cy.add():將ele加入Cytoscape物件中(要Refresh才會出現)。
cy.remove():將Cytoscape物件中的指定ele刪除。
cy.load():將Layout圖層中的所有ele清空並重新讀取Cytoscape物件中的ele顯示。
cy.on():Bind圖層的事件。
cy.center():將圖中心點位置移到所有ele的中心點。
cy.reset():將zoom與pan重新設定。
cy.animate():對視角(zoom and pan)進行動畫移動。
cy.layout():重新設定Layout(傳入Layout options)。
cy.style():重新設定圖層的CSS Style。


集合功能

eles.remove():刪除ele集合。
eles.on():Bind ele集合事件。
eles.removeClass():將ele集合內的每個ele刪除指定class。
eles.addClass():將ele集合內的每個ele加入指定class。
eles.css()(or ele.css()):設定ele集合或單一ele CSS Style。
eles.show():在圖層中顯示ele集合。
eles.hide():在圖層中隱藏ele集合。
eles.each():對ele集合內的每個ele執行指定function。

ele.data():取得指定ele的data屬性物件。
ele.id():取得指定ele的ID屬性。
ele.group():取得指定ele的group屬性。
ele.isNode():判斷該ele是否為Node。
els.isEdge():判斷該ele是否為Edge。

node.position():取得指定node的position屬性物件(位置)。
node.grabbale():判斷此Node是否開放使用者拖拉。
node.grabify():開放此Node讓使用者拖拉。
node.ungrabify():禁止此Node讓使用者拖拉。
node.lock():Lock此Node的Position使其無法更動。
node.unlock():UnLock此Node的Position。
node.isParent():判斷此Node是否為Compound Node的父節點。
node.isChild():判斷此Node是否為Compound Node的子節點。

nodes.children():取得Node的所有直系子節點。
nodes.parents():取得Node的所有父節點(包括父父節點...)。
nodes.descendants():取得Node的所有子節點。
nodes.siblings():取得Node同輩的所有節點(同個parent)。

edge.source():取得Edge的source node。
edge.target():取得Edge的target node。



Event

Cytoscape.js針對許多操作制定了許多事件觸發點,只要Bind此事件並撰寫事件後要做的function就可以在觸發事件後執行客製化的行為,事件的對象可分為使用者輸入事件, ele集合事件, 圖層事件,以下限量也列出幾種常用的事件:


使用者輸入事件

tapstart:點擊滑鼠左鍵開始(剛點下)。
tapdrag:滑鼠左鍵點擊target拖拉。
tagend:點擊滑鼠左鍵結束(放開)。
tap:點擊滑鼠左鍵。
taphold:點擊滑鼠左鍵不放。
cxttapstart:點擊滑鼠右鍵開始(剛點下)。
cxttapend:點擊滑鼠又鍵結束(放開)。
cxttap:點擊滑鼠右鍵。
cxtdrag:滑鼠右鍵點擊target拖拉。

集合事件

add:當ele被加入圖層時觸發。
remove:當ele被移除圖層時觸發。
select:當ele被選取時觸發。
drag:當ele被拖拉移動時觸發。
position:當ele位置改變時觸發。
data:當ele內部data屬性改變時觸發。
style:當ele style改變時觸發。


圖層事件

layoutstart:當layout開始執行時觸發。
layoutready:當layout設定好所有圖形初始化後時觸發。
load:當新圖層重新載入時觸發(cy.load()之後)。
ready:當Cytoscape物件產生後時觸發。

最後,讓我們來看看一個簡單的Cytoscape.js的範例程式,這個範例程式限量加入了讓Compound Node有摺疊與展開子Node的功能。



上述的摺疊功能設計概念是這樣的:因為Cytoscape.js沒有提供Compound Node摺疊的功能,所以要自己寫,而只要利用ele.children()與ele.parent...等API來實作就可以了,然而有一個小問題,因為Compound Node顯示方式是一個大框框將子Node包覆住,這跟網路拓樸圖的表示方式有點不同,所以要為每一個Compound Node製作一個單純顯示用的虛擬Node。舉例來說,Compound Node可以想作是一個資料夾,資料夾內有許多實體檔案或資料夾,因為資料夾不是一個實體而是代表一個集合,所以顯示時我們要用一個虛擬圖示來表示。再來就是利用Javascript物件無限增加屬性的特性,我們在Compound Node的虛擬節點的data加入一個virtaul的屬性,並設定為true,這樣就可以判別是否為虛擬節點,然後再摺疊的function裡在為每個Compound Node加一個fold屬性,這樣才可以判別目前是摺疊還是展開的狀況。

看完了一些Cytoscape.js的簡單Demo之後是不是覺得很有趣,限量當初選擇這套函示庫的原因是Cytoscape算是個老品牌,重點是他的官方網站所提供的文件還蠻淺顯易懂的,而且還有範例程式可以直接在Cytoscape.js的官網上直接Run起來,可以邊Try邊學,就這樣,希望這篇介紹文章能夠幫助到有要畫網路拓譜圖的開發者們。



參考來源:
Cytoscape.js





留言