前言
知識圖譜項目是一個強視覺交互性的關系圖可視化分析系統,很多模塊都會涉及到對節點和關系的增刪改查操作,常規的列表展示類數據通過表格展示,表單新增或編輯,而圖譜類項目通常需要關系圖(力導向圖:又叫力學圖、力導向布局圖,是繪圖的一種算法,關系圖一般采用這種布局方式)去展示,節點和關系的新增編輯也需要前端去做一些復雜的交互設計。除此之外還有節點和關系的各種布局算法,大量數據展示的性能優化,節點動態展開時的局部布局渲染,畫布的可擴展性,樣式的自定義等等諸多技術難點。目前國內使用最多的兩個已開源的前端可視化框架:阿里的AntV、百度的Echarts對於關系圖的支持都比較弱,不能完全滿足項目中的需求。
在之前的兩個圖譜demo項目中我一直是使用的D3.js這個前端最流行的可視化圖庫。D3.js也是比較強大的圖庫,但是它提供的API都是偏底層的,文檔也不友好,比較難上手,實現一個簡單的功能也需要大量的代碼,編碼效率並不是很高,各個版本之間兼容性也很差,並且使用SVG渲染畫布在大量節點顯示的時候有性能瓶頸。
直到我遇見了Cytoscape.js——一個非常適合做知識圖譜的前端可視化圖庫。
先看看cytoscape.js是什么
cytoscape是一個網絡圖的可視化工具,大量的生物分子/基因領域的公司用它來做可視化分析。由於它的通用性,慢慢的也被其他領域的人用來做網絡的可視化和分析。cytoscape分為兩種,一種叫做cytoscape desktop,是一個桌面軟件,可以把數據導入然后生成可視化的網絡圖進行分析;另一種叫做cytoscape.js,是一個javascript庫,主要給開發人員使用,來在網頁上生成可視化的網絡圖。我們要用的是后者。
官方介紹
Cytoscape.js是一個用原生JS編寫的開源圖論(又名網絡)庫。你可以使用Cytoscape.js進行圖形分析和可視化。
Cytoscape.js允許你輕松顯示和操作豐富的交互式圖形。由於Cytoscape.js允許用戶與圖形進行交互,並且庫允許客戶端掛接到用戶事件,因此Cytoscape.js可以輕松集成到你的應用程序中,尤其是因為Cytoscape.js支持桌面瀏覽器(例如Chrome)和移動瀏覽器,就像在iPad上一樣。Cytoscape.js包含了開箱即用的所有手勢,包括捏縮放,框選擇,平移等。
Cytoscape.js還考慮了圖分析:該庫包含圖論中的許多有用功能。你可以在Node.js上無頭使用Cytoscape.js在終端或Web服務器上進行圖形分析。
Cytoscape.js支持許多不同的圖論用例。它支持有向圖,無向圖,混合圖,循環,多圖,復合圖(一種超圖),等等。
兼容所有現代瀏覽器、具有ES5和canvas支持的舊版瀏覽器。
安裝
CND引入
如果要從CDN使用Cytoscape.js,請使用以下CDN之一:
NPM安裝
npm install cytoscape |
Bower安裝
bower install cytoscape |
ESM環境中使用
import cytoscape from 'cytoscape'; |
CommonJS環境中使用
var cytoscape = require('cytoscape'); |
AMD / Require.js一起使用
require(['cytoscape'], function(cytoscape){ // ... }); |
入門和基本用法
<!DOCTYPE> <html> <head> <title>cytoscape demo</title> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script> <style> body { font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif; font-size: 14px } #cy { position: absolute; left: 0; top: 0; bottom: 0; right: 0; z-index: 1; } h1 { opacity: 0.5; font-size: 1em; font-weight: bold; } </style> <script> document.addEventListener('DOMContentLoaded', function () { var cy = window.cy = cytoscape({ container: document.getElementById('cy'), style: [ { selector: 'node', style: { 'content': 'data(id)' } }, { selector: 'edge', style: { 'curve-style': 'bezier', 'target-arrow-shape': 'triangle' } } ], elements: { nodes: [ { data: { id: 'a' } }, { data: { id: 'b' } } ], edges: [ { data: { id: 'ab', source: 'a', target: 'b' } } ] }, layout: { name: 'grid' } }); }); </script> </head> <body> <h1>cytoscape demo</h1> <div id="cy"></div> </body> </html> |
架構和API
使用Cytoscape.js,開發者需要關注體系結構中的兩個組件,即核心(即圖形實例)和集合。在Cytoscape.js中,核心是開發者進入庫的主要入口點。開發者可以從整體上運行布局,更改視口並在整個圖形上執行其他操作。
核心提供了幾種訪問圖中元素的函數。這些函數中的每一個都返回一個集合,即圖形中的一組元素。集合上有可用的函數,這些函數使開發者可以過濾集合,對集合執行操作,遍歷有關集合的圖,獲取有關集合中元素的數據等等。
有幾種類型可以執行不同的功能,在文檔中用來表示這些類型的變量名概述如下:
變量名 | 功能描述 |
---|---|
cy |
核心 |
eles |
一個或多個元素(節點和邊)的集合 |
ele |
單個元素(節點或邊)的集合 |
nodes |
一個或多個節點的集合 |
node |
單節點的集合 |
edges |
一個或多個邊的集合 |
edge |
單邊的集合 |
layout |
布局 |
ani |
動畫 |
默認情況下,函數會將引用返回給調用對象,以允許鏈接(例如obj.fn1().fn2().fn3()
)。除非另有說明,否則函數可以這種方式鏈接,除非指定了不同的返回值。這適用於核心和集合。
核心
核心對象是圖形的界面。這是進入Cytoscape.js的入口:通過此對象可以訪問庫的所有功能。
創建核心:
var cy = cytoscape({ /* options */ }); |
你可以初始化內核,而無需任何選項。如果要將Cytoscape用作可視化對象,則需要container
DOM元素,例如:
var cy = cytoscape({ container: document.getElementById('cy') }); |
集合
集合包含一組節點和邊。調用函數會將函數應用於集合中的所有元素。從集合中讀取值時,例如eles.data()將返回集合中第一個元素的值。例如:
var weight = cy.nodes().data("weight"); console.log( cy.nodes()[0].data("weight") + ' == ' + weight ); // weight is the first ele's weight |
通過使用選擇器將集合縮小到一個元素(即eles.size() === 1)或eles.eq()函數,可以確保你正在讀取所需的元素。
初始化選項
Cytoscape.js實例具有許多可以在初始化時設置的選項。下面概述了它們的默認值。
請注意,所有內容都是可選的。默認情況下,你將獲得帶有默認樣式表的空圖。為了方便起見,瀏覽器外部的環境(例如Node.js)自動設置為無頭。
var cy = cytoscape({ // very commonly used options container: undefined, elements: [ /* ... */ ], style: [ /* ... */ ], layout: { name: 'grid' /* , ... */ }, // initial viewport state: zoom: 1, pan: { x: 0, y: 0 }, // interaction options: minZoom: 1e-50, maxZoom: 1e50, zoomingEnabled: true, userZoomingEnabled: true, panningEnabled: true, userPanningEnabled: true, boxSelectionEnabled: true, selectionType: 'single', touchTapThreshold: 8, desktopTapThreshold: 4, autolock: false, autoungrabify: false, autounselectify: false, // rendering options: headless: false, styleEnabled: true, hideEdgesOnViewport: false, textureOnViewport: false, motionBlur: false, motionBlurOpacity: 0.2, wheelSensitivity: 1, pixelRatio: 'auto' }); |
非常常用的選項:
container
:一個HTML DOM元素,呈現圖形的容器。如果Cytoscape.js無頭運行,則未指定。該容器應為空div。
elements
:指定為純對象的元素數組。為了方便起見,也可以將此選項指定為可解析為JSON元素的Promise。
style
:用於設置圖形樣式的樣式表。為了方便起見,也可以將此選項指定為解析為樣式表的Promise。
layout
:指定布局選項的普通對象。該name
字段最初指定運行哪個布局。有關布局支持的選項,請參考布局的文檔。如果要在元素JSON中自己指定節點位置,則可以使用preset
布局-默認情況下,它不設置任何位置,而將節點保留在當前位置(即options.elements
在初始化時指定)。
初始視口狀態:
zoom
:圖形的初始縮放級別。確保fit
在布局中禁用視口操縱選項,例如,以便在應用布局時不會覆蓋它。你可以設置options.minZoom
和options.maxZoom
設置縮放級別的限制。
pan
:圖形的初始平移位置。請確保fit
在布局中禁用視口操縱選項,例如,以便在應用布局時不會覆蓋它。
互動選項:
minZoom
:圖形縮放級別的最小界限。視口的縮放比例不能小於此縮放級別。
maxZoom
:圖形的縮放級別的最大界限。視口的縮放比例不能大於此縮放級別。
zoomingEnabled
:是否通過用戶事件和以編程方式啟用了縮放圖形。
userZoomingEnabled
:是否允許用戶事件(例如,鼠標滾輪,捏縮放)縮放圖形。縮放的程序更改不受此選項的影響。
panningEnabled
:是否通過用戶事件和以編程方式啟用了平移圖表。
userPanningEnabled
:是否允許用戶事件(例如,拖動圖形背景)來平移圖形。平移的編程更改不受此選項的影響。
boxSelectionEnabled
:是否啟用了框選擇(即,拖動框疊加並釋放它以進行選擇)。如果在啟用了平移的同時啟用了該功能,則用戶必須使用修飾鍵(Shift,Alt,Control或Command)才能使用框選擇。
selectionType
:一個字符串,指示用戶輸入的選擇行為。對於'additive'
,用戶做出的新選擇將添加到當前選擇的元素集中。對於'single'
,用戶進行的新選擇將成為當前所選元素的整個集合(即,未選擇先前的元素)。
touchTapThreshold
&desktopTapThreshold
:一個非負整數,指示用戶在輕擊手勢期間分別在觸摸設備和台式設備上可以移動的最大允許距離。這使用戶更容易點擊。這些值具有默認值,因此除非有充分的理由,否則不建議更改這些選項。較大的值幾乎肯定會產生不良后果。
autoungrabify
:默認情況下是否應取消節點的節點化(用戶不可抓取)(如果true
,則覆蓋單個節點狀態)。
autolock
:默認情況下是否應鎖定節點(完全不可拖動)(如果true
,則覆蓋單個節點狀態)。
autounselectify
:默認情況下是否應取消選擇節點(不可變的選擇狀態)(如果true
,則覆蓋單個元素的狀態)。
渲染選項:
headless
:一個方便的選項,用於初始化實例以使其無頭運行。你無需在隱式無頭的環境(例如Node.js)中進行設置。但是,headless: true
如果要在瀏覽器中使用無頭實例,則設置起來很方便。
styleEnabled
:一個布爾值,指示是否應使用樣式。對於無頭(即,在瀏覽器外部)環境,不需要顯示,也不需要樣式設置,從而加快了代碼的速度。如果在特殊情況下需要樣式,則可以在無頭環境中手動啟用樣式。請注意,如果計划渲染圖形,則禁用樣式沒有意義。還要注意,cy.destroy()
必須調用來清理啟用樣式的無頭實例。
hideEdgesOnViewport
:渲染提示,當設置為時,true
將使渲染器在操縱視口時不渲染邊緣。這使得平移,縮放,拖動等等對於大型圖形的響應更加靈敏。由於性能增強,現在該選項幾乎沒有意義。
textureOnViewport
:一種渲染提示,設置為時,true
使渲染器在平移和縮放過程中使用紋理而不是繪制元素,從而使大型圖形更具響應性。由於性能增強,現在該選項幾乎沒有意義。
motionBlur
:渲染提示,設置為時,true
使渲染器使用運動模糊效果使幀之間的過渡看起來更平滑。這可以提高大型圖的感知性能。由於性能增強,現在該選項幾乎沒有意義。
motionBlurOpacity
:當時motionBlur: true
,此值控制運動模糊幀的不透明度。較高的值會使運動模糊效果更加明顯。由於性能增強,現在該選項幾乎沒有意義。
wheelSensitivity
:更改變焦時的滾輪靈敏度。這是一個乘法修飾符。因此,介於0和1之間的值會降低靈敏度(縮放較慢),而大於1的值會提高靈敏度(縮放較快)。此選項設置為一個合理的值,該值對於Linux,Mac和Windows上的主流鼠標(Apple,Logitech,Microsoft)非常有效。如果默認值在你的特定系統上看起來太快或太慢,則你的操作系統或專用鼠標可能具有非默認鼠標設置。除非你的應用只能在特定硬件上運行,否則你不應更改此值。否則,冒着使大多數用戶縮放得太慢或太快的風險。
pixelRatio
:使用手動設置的值(1.0
建議,如果設置)覆蓋屏幕像素比率。通過減少需要渲染的有效區域,可以將其用於提高高密度顯示器的性能,盡管在最新的瀏覽器版本中,此需求要小得多。如果要使用硬件的實際像素比率,則可以設置pixelRatio: 'auto'
(默認)。
下面着重介紹一下cytoscape.js中幾個非常重要且功能強大的模塊,也是我在知識圖譜項目中使用最多的模塊。
一、選擇器
一些注意事項:選擇器的功能類似於DOM元素上的CSS選擇器,但Cytoscape.js中的選擇器可用於圖形元素的集合。注意,如果選擇器被指定為函數的參數,那么可以使用els .filter()樣式的過濾器函數來代替選擇器
cy.$('#j').neighborhood(function( ele ){ return ele.isEdge(); }); |
選擇器可以組合在一起,以在Cytoscape.js中進行強大的查詢,例如:
// get all nodes with weight more than 50 and height strictly less than 180 cy.elements("node[weight >= 50][height < 180]"); |
選擇器可以用逗號連接在一起(有效地創建邏輯或):
// get node j and the edges coming out from it cy.elements('node#j, edge[source = "j"]'); |
重要的是要注意,字符串需要用引號引起來:
//cy.filter('node[name = Jerry]'); // this doesn't work cy.filter('node[name = "Jerry"]'); // but this does |
請注意,對於ID,字段名稱等,需要轉義一些字符:
cy.filter('#some\\$funky\\@id'); |
這些字符的一些示例包括( ^ $ \ / ( ) | ? + * [ ] { } , . )
。盡量避免使用非字母數字字符作為字段名稱和ID,以使事情變得簡單。如果必須對ID使用特殊字符,請使用數據選擇器而不是ID選擇器:
cy.filter('[id = "some$funky@id"]'); |
組、類、ID選擇器
node
, edge
或*
(組選擇器) 基於組匹配元素(node對應
節點,edge
對應邊緣,*
對應所有)。
.className
匹配具有指定類的元素(例如,.foo
用於名為“ foo”的類的元素)。
#id
匹配具有匹配ID的元素(例如#foo等效於
[id = 'foo']
)
數據選擇器
[name]
如果元素具有定義的指定數據屬性(即未定義)undefined
(例如,[foo]
對於名為“ foo”的屬性),則匹配元素。在此,null
被認為是定義值。
[^name]
如果未定義指定的數據屬性undefined
(例如[^foo]
),則匹配元素。在此,null
被認為是定義值。
[?name]
如果指定的data屬性為真值(例如[?foo]
),則匹配元素。
[!name]
如果指定的data屬性為falsey值,則匹配元素(例如[!foo]
)。
[name = value]
如果元素的數據屬性與指定值匹配(例如[foo = 'bar']
或[num = 2]
),則匹配元素。
[name != value]
如果元素的數據屬性與指定值不匹配(例如[foo != 'bar']
或[num != 2]
),則匹配元素。
[name > value]
如果元素的數據屬性大於指定值(例如[foo > 'bar']
或[num > 2]
),則與之匹配。
[name >= value]
如果元素的數據屬性大於或等於指定值(例如[foo >= 'bar']
或[num >= 2]
),則匹配它們。
[name < value]
如果元素的數據屬性小於指定值(例如[foo < 'bar']
或[num < 2]
),則匹配它們。
[name <= value]
如果元素的數據屬性小於或等於指定值(例如[foo <= 'bar']
或[num <= 2]
),則匹配它們。
[name *= value]
如果元素的數據屬性包含指定的值作為子字符串(例如[foo *= 'bar']
),則與之匹配。
[name ^= value]
如果元素的數據屬性以指定值(例如[foo ^= 'bar']
)開頭,則與之匹配。
[name $= value]
如果元素的數據屬性以指定值(例如[foo $= 'bar']
)結尾,則與之匹配。
@
(數據屬性操作者調節劑) 預先考慮的操作,使得不區分大小寫(例如[foo @$= 'ar']
,[foo @>= 'a']
,[foo @= 'bar']
)
!
(數據屬性運算符修飾符) 加在運算符之前,以便取反(例如[foo !$= 'ar']
,[foo !>= 'a']
)
[[]]
(元數據括號) 使用雙方括號代替方括號,以匹配元數據而不是數據(例如,[[degree > 2]]
匹配度數大於2的元素)。所支持的特性包括degree
,indegree
,和outdegree
。
復合選擇器
>
(子選擇器) 匹配父節點的直接子代(例如node > node
)。
空格(后代選擇器) 匹配父節點的后代(例如node node
)。
$
(主題選擇器) 設置選擇器的主題(例如$node > node
,選擇父節點而不是子節點)。
狀態選擇器
動畫
:animated
:匹配當前正在設置動畫的元素。:unanimated
:匹配當前未設置動畫的元素。
選擇
:selected
:匹配選定的元素。:unselected
:匹配未選中的元素。:selectable
:匹配可選元素。:unselectable
:匹配無法選擇的元素。
鎖定
:locked
:匹配鎖定的元素。:unlocked
:匹配未鎖定的元素。
樣式
:visible
:匹配可見的元素(例如display: element
和visibility: visible
)。:hidden
:匹配隱藏的元素(即display: none
或visibility: hidden
)。:transparent
:匹配透明的元素(例如,opacity: 0
針對自己或父母)。:backgrounding
:如果當前正在加載其背景圖像,則匹配該元素。:nonbackgrounding
:如果當前未加載背景圖片,則匹配該元素;即沒有圖像或圖像已經加載)。
用戶互動:
:grabbed
:匹配用戶抓取的元素。:free
:匹配用戶當前未捕獲的元素。:grabbable
:匹配用戶可抓取的元素。:ungrabbable
:匹配用戶不可抓取的元素。:active
:匹配活動的元素(即用戶交互,類似於:active
CSS)。:inactive
:匹配無效的元素(即無用戶交互)。:touch
:在基於觸摸的環境中(例如在平板電腦上)顯示時,匹配元素。
圖表內或圖表外
:removed
:匹配從圖中刪除的元素。:inside
:匹配圖中的元素(不會刪除它們)。
復合節點
:parent
:匹配父節點(它們具有一個或多個子節點)。:childless
:匹配無子節點(它們有零個子節點)。:child
或:nonorphan
:匹配子節點(它們每個都有一個父節點)。:orphan
:匹配孤立節點(每個節點都沒有父節點)。:compound
:匹配父節點。還匹配連接到父節點的邊(每個邊在源節點和目標節點中至少有一個父節點)。
邊緣
:loop
:匹配循環邊(與目標源相同)。:simple
:匹配簡單邊緣(即,就像在簡單圖形中一樣,將不同的源作為目標)。
二、樣式
Cytoscape.js中的樣式盡可能遵循CSS約定。在大多數情況下,屬性與其對應的CSS同名具有相同的名稱和行為。但是,CSS中的屬性不足以指定圖形某些部分的樣式。在這種情況下,將引入Cytoscape.js特有的其他屬性。
為了簡化和易於使用,在樣式表中完全忽略了特異性規則。對於給定元素的給定樣式屬性,最后一個匹配的選擇器獲勝。
示例
字符串格式: cytoscape({ container: document.getElementById('cy'), // ... style: 'node { background-color: green; }' // probably previously loaded via ajax rather than hardcoded // , ... }); JSON格式: cytoscape({ container: document.getElementById('cy'), // ... style: [ { selector: 'node', style: { 'background-color': 'red' } } // , ... ] // , ... }); 函數格式: cytoscape({ container: document.getElementById('cy'), // ... style: cytoscape.stylesheet() .selector('node') .style({ 'background-color': 'blue' }) // ... // , ... }); |
還可以選擇使用css
代替style
,例如.selector( ... ).css( ... )
或{ selector: ..., css: ... }。
三、事件
事件冒泡
所有發生在元素上的事件都會冒泡到復合父元素,然后到達核心。當你在監聽核心的時候,你必須考慮到這一點,這樣你才能區分發生在背景上的事件和發生在元素上的事件。使用eventObj.target表示事件的發起者(即eventObj.target=== cy || eventObj.target === someEle)。
用戶輸入設備事件
這些是正常的瀏覽器事件,你可以通過Cytoscape.js進行監聽。你可以在核心和集合上監聽這些事件。
mousedown
:按下鼠標按鈕時mouseup
:釋放鼠標按鈕時click
:單擊鼠標按鈕時mouseover
:將光標放在目標上方時mouseout
:當光標從目標移出時mousemove
:當光標移動到目標上方的某個位置時touchstart
:當一根或多根手指開始觸摸屏幕時touchmove
:當一個或多個手指在屏幕上移動時touchend
:從屏幕上移開一根或多根手指時
你還可以使用一些更高級別的事件,因此你不必為鼠標輸入設備和觸摸設備監聽其他事件。
tapstart
或vmousedown
:標准化的點擊啟動事件tapdrag
或vmousemove
:標准化的使用鼠標左鍵拖拽移動事件tapdragover
:拖拽到元素之上tapdragout
:拖拽到元素之外tapend
或vmouseup
:標准化的點擊結束事件tap
或vclick
:標准化的點擊事件taphold
:標准化的點擊保持事件cxttapstart
:標准化的右鍵單擊或兩指輕敲開始cxttapend
:標准化的右鍵單擊或兩指輕敲結束cxttap
:正常右擊或兩指輕敲cxtdrag
:使用鼠標右鍵移動或兩指拖動cxtdragover
:通過遍歷節點時正常右擊或兩指輕敲cxtdragout
:通過離開節點時正常右擊或兩指輕敲boxstart
:開始選擇框時boxend
:結束框選擇時boxselect
:由框選擇選擇時在元素上觸發box
:在框內打開時在元素上觸發boxend
集合事件
這些事件是Cytoscape.js自定義的。你可以在集合上監聽這些事件。
- tapstart或vmousedown:標准化的輕按開始事件(mousedown或touchstart)
- tapdrag或vmousemove:規范化的移動事件(touchmove或mousemove)
- tapdragover:在元素事件之上的標准化事件(touchmove或mousemove / mouseover)
- tapdragout:在元素事件之外的標准化事件(touchmove或mousemove / mouseout)
- tapend或vmouseup:標准化的點擊結束事件(mouseup或touchend)
- tap或vclick:標准化的點擊事件(單擊或touchstart,然后是touchend,而沒有touchmove)
- taphold:標准化的鼠標保持事件
- cxttapstart:標准化的右鍵單擊按下或兩指點擊
- cxttapend:標准化的右鍵單擊抬起或兩指點擊
- cxttap:標准化的右鍵單擊或兩指點擊
- cxtdrag:在cxttapstart之后但在cxttapend之前進行標准化的鼠標移動或兩指拖動
- cxtdragover:鼠標右鍵拖拽經過節點時
- cxtdragout:鼠標右鍵拖拽離開節點時
- boxstart:開始選擇框時
- boxend:結束框選擇時
- boxselect:通過框選選擇時在元素上觸發
- box:在boxend的框內時在元素上觸發
圖事件
這些事件是Cytoscape.js的自定義事件,它們發生在核心上。
layoutstart
:布局開始運行時layoutready
:當布局已為所有節點設置初始位置(但可能未設置最終位置)時layoutstop
:版式完全完成運行或停止運行時ready
:當准備與Cytoscape.js的新實例進行交互時destroy
:當調用顯式銷毀Cytoscape.js實例時.destroy()
。render
:(重新)渲染視口時pan
:平移視口時zoom
:縮放視口時viewport
:更改視口時(例如pan
,zoom
在縮放某個點時從,或從兩者同時縮放-例如,捏到縮放)resize
:調整視口大小時(通常通過調用cy.resize()
,window
調整大小或在Cytoscape.js div上切換類)
四、布局
布局的功能是設置圖中節點上的位置。布局是Cytoscape.js的擴展,因此任何人都可以編寫布局而無需修改庫本身。
默認情況下,Cytoscape.js包含幾種布局,它們的選項都指定了默認值。請注意,必須設置options.name
布局的名稱以指定要運行的布局。
每個布局都有其自己的算法,用於設置每個節點的位置。此算法影響圖形的整體形狀和邊的長度。可以通過設置布局的選項來自定義布局的算法。因此,可以通過適當設置布局選項來控制邊緣長度。
對於力導向(物理)布局,通常可以選擇為每個邊緣設置權重以影響相對邊緣長度。邊緣長度也可能受諸如間距系數,角度和避免重疊等選項的影響。設置邊緣長度取決於特定的布局,某些布局將允許比其他布局更精確的邊緣長度。
Cytoscape.js自帶了一下幾種布局:
null :空布局將所有節點放在(0,0)處,這對於調試非常有用。
let options = { name: 'null', ready: function(){}, // on layoutready stop: function(){} // on layoutstop }; cy.layout( options ); |
random :隨機布局將節點放置在視口內的隨機位置。
let options = { name: 'random', fit: true, // whether to fit to viewport padding: 30, // fit padding boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } animate: false, // whether to transition the node positions animationDuration: 500, // duration of animation in ms if enabled animationEasing: undefined, // easing of animation if enabled animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts ready: undefined, // callback on layoutready stop: undefined, // callback on layoutstop transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts }; cy.layout( options ); |
preset :預設布局將節點放置在手動指定的位置。
let options = { name: 'preset', positions: undefined, // map of (node id) => (position obj); or function(node){ return somPos; } zoom: undefined, // the zoom level to set (prob want fit = false if set) pan: undefined, // the pan level to set (prob want fit = false if set) fit: true, // whether to fit to viewport padding: 30, // padding on fit animate: false, // whether to transition the node positions animationDuration: 500, // duration of animation in ms if enabled animationEasing: undefined, // easing of animation if enabled animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts ready: undefined, // callback on layoutready stop: undefined, // callback on layoutstop transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts }; cy.layout( options ); |
該布局有一個非常實用的地方是,當你保存畫布並保存了節點元素的位置后,再加載已保存的畫布的時候,可以采用此布局方式。
grid :網格布局將節點放置在一個間隔均勻的網格中。
let options = { name: 'grid', fit: true, // whether to fit the viewport to the graph padding: 30, // padding used on fit boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space avoidOverlapPadding: 10, // extra spacing around nodes when avoidOverlap: true nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up condense: false, // uses all available space on false, uses minimal space on true rows: undefined, // force num of rows in the grid cols: undefined, // force num of columns in the grid position: function( node ){}, // returns { row, col } for element sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') } animate: false, // whether to transition the node positions animationDuration: 500, // duration of animation in ms if enabled animationEasing: undefined, // easing of animation if enabled animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts ready: undefined, // callback on layoutready stop: undefined, // callback on layoutstop transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts }; cy.layout( options ); |
circle :圓圈布局將節點放在圓圈中。
let options = { name: 'circle', fit: true, // whether to fit the viewport to the graph padding: 30, // the padding on fit boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } avoidOverlap: true, // prevents node overlap, may overflow boundingBox and radius if not enough space nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up radius: undefined, // the radius of the circle startAngle: 3 / 2 * Math.PI, // where nodes start in radians sweep: undefined, // how many radians should be between the first and last node (defaults to full circle) clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false) sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') } animate: false, // whether to transition the node positions animationDuration: 500, // duration of animation in ms if enabled animationEasing: undefined, // easing of animation if enabled animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts ready: undefined, // callback on layoutready stop: undefined, // callback on layoutstop transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts }; cy.layout( options ); |
concentric :同心圓布局將節點放置在同心圓中,基於你指定的將節點隔離到各個級別的度量。此布局在ele.scratch()中設置同心值。
let options = { name: 'concentric', fit: true, // whether to fit the viewport to the graph padding: 30, // the padding on fit startAngle: 3 / 2 * Math.PI, // where nodes start in radians sweep: undefined, // how many radians should be between the first and last node (defaults to full circle) clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false) equidistant: false, // whether levels have an equal radial distance betwen them, may cause bounding box overflow minNodeSpacing: 10, // min spacing between outside of nodes (used for radius adjustment) boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm height: undefined, // height of layout area (overrides container height) width: undefined, // width of layout area (overrides container width) spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up concentric: function( node ){ // returns numeric value for each node, placing higher nodes in levels towards the centre return node.degree(); }, levelWidth: function( nodes ){ // the letiation of concentric values in each level return nodes.maxDegree() / 4; }, animate: false, // whether to transition the node positions animationDuration: 500, // duration of animation in ms if enabled animationEasing: undefined, // easing of animation if enabled animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts ready: undefined, // callback on layoutready stop: undefined, // callback on layoutstop transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts }; cy.layout( options ); |
breadthfirst :廣度優先布局基於圖的廣度優先遍歷將節點置於層次結構中。
cose :cose(復合彈簧嵌入器)布局使用物理模擬對圖形進行布局。 它適用於非復合圖,並且具有附加邏輯以很好地支持復合圖。
五、第三方擴展
UI擴展
-
anywhere-panning
:在節點或邊緣上拖動時允許平移。 -
automove
:根據指定的規則(例如,同步節點移動,約束移動等)自動更新節點位置 -
autopan-on-drag
:將節點拖動到視口邊界之外時,將自動平移視口。 -
canvas
:在Cytoscape圖形上方或下方創建畫布的擴展。用於自定義節點/邊線,工程圖背景等。 -
cerebralweb
:可以對基於亞細胞定位或其他自定義注釋進行分層的分子相互作用網絡進行快速且交互式的可視化。 -
compound-drag-and-drop
:用於添加和刪除子項的復合節點拖放UI -
context-menus
:傳統的右鍵菜單 -
cxtmenu
:圓形上下文菜單,允許在圖形上進行一次輕掃命令。 -
edge-bend-editing
:用於編輯邊緣折彎(段邊緣和貝塞爾曲線邊緣)的UI -
edge-editation
:向節點添加句柄,並允許創建不同類型的邊緣 -
edge-connections
:根據數據的關聯模型,允許邊緣在視覺上連接其他邊緣。 -
edgehandles
:用於連接具有邊緣的節點的UI。 -
even-parent
:無論有多少子元素,都可以調整子元素的尺寸以適合父元素的布局。 -
expand-collapse
:提供用於擴展和折疊復合父節點的API -
grid-guide
:向Cytoscape圖形添加網格和捕捉功能 -
navigator
:圖形的鳥瞰小部件。 -
no-overlap
:防止節點在拖動時重疊。 -
node-html-label
:允許將HTML指定為節點的標簽。 -
node-resize
:具有傳統UI的高度可定制的節點大小調整擴展。 -
noderesize
:簡約的節點調整大小控件。 -
panzoom
:一個panzoom UI小部件。 -
popper
:Popper.js的包裝器,使你可以相對於Cytoscape元素定位div(可與Tippy.js一起使用以創建工具提示)。 -
qtip
:一個包裝器,可讓你在圖形元素或圖形背景上使用qTips。 -
snap-to-grid
:向Cytoscape.js圖添加網格捕捉和網格線。 -
supportimages
:在Cytoscape.js上支持圖像。 -
toolbar
:允許用戶創建自定義工具欄,以添加到Cytoscape核心實例的旁邊。
布局擴展
-
cola
:Cola.js物理模擬布局。cola可以產生漂亮的布局效果,動畫效果非常流暢,並且具有控制布局的絕佳選擇。 -
avsdf
:AVSDF布局。它將節點組織成一個圓形,並嘗試盡可能減少邊緣交叉。 -
cise
:CiSE布局創建圓形簇,並使用物理模擬創建簇之間的距離。 -
cose-bilkent
:Bilkent的CoSE布局,具有增強的復合節點布局。CoSE Bilkent可以提供近乎完美的最終結果。 -
dagre
:用於DAG和樹的Dagre布局。 -
elk
:用於Cytoscape.js的ELK布局算法適配器。 -
euler
:Euler是一種快速,小文件大小,高質量的力導向(物理模擬)布局。它對於非復合圖非常有用,並且對復合圖具有基本支持。 -
fcose
:fCoSE布局是CoSE-Bilkent布局的更快版本。它支持復合圖和非復合圖,為力導向布局提供頂級的最終結果和高性能。 -
klay
:Klay是適用於大多數圖形類型的布局。它為普通圖提供了良好的結果,並且可以很好地處理DAG和復合圖。 -
ngraph.forcelayout
:一種物理模擬布局,在平面圖上特別有效。它相對較快。 -
polywas
:GWAS(全基因組關聯研究)數據的布局,說明了基因座之間的關系。 -
spread
:快速的擴展物理模擬布局。它嘗試使用所有視口空間,但可以對其進行配置以產生更緊密的結果。最初使用Fruchterman-Reingold,在傳播階段使用Gansner和North。 -
springy
:Springy物理模擬布局。這是基本的物理布局。
API擴展
-
all-paths
:獲取所有最長的定向路徑。 -
clipboard
:將復制粘貼實用程序添加到Cytoscape.js。 -
dblclick
:向Cytoscape.js 添加雙擊事件。 -
graphml
:將GraphML導入和導出功能添加到Cytoscape.js。 -
undo-redo
:將撤銷重做API添加到Cytoscape.js。 -
view-utilities
:向Cytoscape.js添加搜索和突出顯示API。
工具包
-
cytosnap
:一個Node.js程序包,該程序包使用Puppeteer在服務器上呈現Cytoscape.js圖的圖像。 -
ngx-cytoscape
:Cytoscape.js的Angular 5+組件。 -
react-cytoscapejs
:用於Cytoscape.js網絡可視化的React組件。 -
sif.js
:一個用於解析簡單交互文件(SIF)文件的JavaScript庫。 -
sbgn-stylesheet
:為SBGN預設的樣式表。 -
sbgnml-to-cytoscape
:將基於XML的SBGN文件轉換為Cytoscape.js JSON。 -
vue-cytoscape
:Cytoscape.js 的Vue組件。
總結:
cytoscape.js本身是一個開源的用來做圖形分析和可視化的JS圖論(又名網絡)庫(graph theory (a.k.a. network)),大量的生物分子和醫療領域的公司或者科研機構在使用。
因為它主要是做網絡圖或者叫關系圖分析,所以非常適合做知識圖譜項目,它功能齊全,且使用簡單,內置了很多常用的布局算法,路徑算法,復合圖形,還有豐富的第三方插件,基本上能滿足你在項目中的各種需求。
需要關注兩個最主要的功能組件----核心(圖形實例)和集合,提供了大量的方法供開發者調用去實現你想實現的功能,並且繼承了JQuery的很多功能比如選擇器、鏈式調用、甚至連設置css樣式都和JQuery如出一轍。所以上手它並熟練使用它不是一件特別難的事。
我現在也只是使用了它很少一部分的功能,大量的高級用法還需要繼續深入研究。
本文大量內容都是節選自官方文檔翻譯過來的,不免會有不太嚴謹的地方歡迎指出,如果你也對它感興趣可以訪問它的官方文檔和github主頁:
官方文檔:https://js.cytoscape.org/
github主頁:https://github.com/cytoscape/cytoscape.js