前幾天剛寫下了一篇關於介紹G6的博客,有興趣的能夠看一下。後面回想起來發現當時把篇幅都花在介紹G6的核心概念上面了,真正實戰和後面問題部分的總結還不夠完善。所以再寫一篇博客來補充一下。node
正如前篇提到的,在實現的時候遇到一些關於渲染性能和佈局方面的問題。那就一步步來先從性能方面介紹吧。git
因爲需求涉及大量數據的渲染,剛開始作demo的時候模擬了1000+的數據,測試一下G6樹圖佈局的渲染性能。第一版的demo寫好以後發現渲染效果挺差的,啓動的時候竟然有9秒的白屏時間。而在官方提供的一個性能demo,節點數量50000+的狀況下才出現交互上的卡頓,並且首屏的渲染加上網絡的請求時間加起來也不過8s左右。因而打開了Performance找一下問題出在哪一個地方。github
從上面的性能截圖發現,pathFinder這個方法耗時了6s多。重複調試發現這個函數平均耗時都在6到7s。那咱們的優化就要從這個函數入手了。這是Antv G6/edge包下的一個函數,說明咱們的耗時跟樹圖的邊有關係。後面查閱github上的issue和官方文檔找到了答案,當咱們的邊類型設置爲polyline時,默認會根據 A* 算法自動生成折線,而這個算法的時間複雜的挺高的。咱們嘗試着切換一下edge的type看一下效果。總體的耗時在1s左右。算法
咱們經過兩張gif更直觀地感覺一下優化的效果。canvas
優化前:性能優化
優化後:
markdown
不過當咱們確實須要折線的樣式又不想使用性能消耗比較大的polyline時,能夠採用自定義edge的方式生成折線的樣式, 這種方式的渲染表現基本與內置邊一致。這裏簡略貼下自定義折線的核心代碼。網絡
G6.registerEdge(
"h-poly-line",
{
draw(cfg, group) {
const { startPoint, endPoint } = cfg;
const shape = group.addShape("path", {
attrs: {
stroke: "#333",
path: [
["M", startPoint.x, startPoint.y],
["L", endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y], // 三分之一處
["L", endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y], // 三分之二處
["L", endPoint.x, endPoint.y],
],
},
name: "h-poly-line",
});
return shape;
},
update: undefined //這裏須要重寫update否則默認繼承line的方法
},
"line"
);
複製代碼
使用的時候只須要在實例化Graph的配置中指定edge的type便可。數據結構
const graph = new G6.Graph({
...
defaultEdge: {
type: 'h-poly-line' //指定邊的類型
}
...
})
複製代碼
官方還提供了另一種優化渲染的方案,就是經過 Web-Worker 機制去渲染,而不阻塞頁面的其餘部分用戶交互。使用上也很簡單一樣是在config中配置便可。函數
一樣是數據量1000+的狀況,咱們的demo在拖拽和縮放的交互上面會出現卡頓。前面提到了官方的性能例子並無出現這個問題,主要是由於官方的例子中使用了內置的節點,因爲要知足展現更多比賽信息的需求,demo則是使用了自定義的節點。這致使了咱們在交互的時候要花更多的時間去從新渲染整個節點。官方給這方面的狀況提供了一個優化的方法, 代碼以下。在節點交互的時候,不渲染除keyShape外的Shape。雖然帶來更好的性能,可是開啓這個功能後用戶的視覺體驗是會比較差的。因此使用這個方案還要考慮清楚。
const graph = new G6.Graph({
...
modes: {
default: [
"drag-canvas",
{
type: "zoom-canvas",
enableOptimize: true //開啓性能優化
}
],
},
...
})
複製代碼
另外就是從node入手優化了,減小節點中沒必要要的Shape,這個要根據實際狀況分析,這裏就不展開了。
另外一個比較難處理的問題就是佈局問題,內置的樹圖佈局並不能計算出節點準確的座標,致使節點重疊在一塊兒。下面是採用默認參數下的緊湊樹圖佈局效果。
目前是經過主動去設定間隔和節點寬高去調整佈局,但我的感受這種方法不是特別的優雅。配置的代碼以下:
const graph = new G6.Graph({
...
layout: {
type: "compactBox",
direction: direction,
//下面四個屬性配置佈局中的節點寬高和層級間隔
//官方文檔中這幾個屬性的function|number
//不過在4.3.4版本下使用number會報錯
//參數爲節點信息
getWidth: ({type, size}) => {
if(type === "race-node"){
return size[0]
}
return 200
},
getHeight: ({type, size}) => {
if(type === "race-node"){
return size[1]
}
return 100
},
getHGap: ({type}) => type === 'race-node'? 25 : 10,
getVGap: ({type}) => type === 'race-node'? 25 : 100
}
...
})
複製代碼
修改配置後的效果以下:
翻閱文檔和實現過程當中發現,樹圖佈局仍是有比較多的限制。樹圖佈局並不支持放在子圖佈局中,也不支持Web-Worker優化。因爲它跟通常圖的數據結構不一致,二者不能在畫板上混用。另外樹圖佈局中也不能使用Combo(即將多個節點歸爲一組)。根據官方的說法,上面這幾個功能在短期內也不打算支持。在某些狀況咱們可能須要操做樹圖中某一層級的數據,因爲沒有Combo這就很難實現了,咱們只能用其餘的佈局去模擬樹圖,好比說用Darge佈局去實現。
Darge佈局不像樹圖同樣能夠根據數據結構生成節點關係這一點須要另外生成,其它的用法二者都比較相似,性能表現方面也比較相近,Darge佈局基本上能夠還原通常的樹圖佈局。
// 緊湊樹佈局 規定的數據類型
const compactBoxData = [
{
id: '1',
children: [
{
id: '2',
children: []
}
...
]
}
]
// 加載數據
graph.data(compactBoxData)
//Darge佈局
//須要將元數據轉換成下列形式
const dargeData = {
nodes: [
{
id: '1'
},
{
id: '2'
}
...
],
edges: [
{
source: '1',
target: '2'
}
]
}
// 加載數據
graph.data(dargeData)
複製代碼
完整的代碼實現能夠看官方的demo。
另外還要吐槽一下,類JSX語法定義節點的方式使用體驗上不如原來的addShapeAPI,並不能很好的描述Shape間的關係,嵌套層級過深的話會出現重疊等佈局bug。
以上就是目前遇到的一些坑。雖然有些小的問題,但在社區主流可視化方案中,G6更加適合圖可視化的場景,開箱即用同時又有較好的擴展性,提供的佈局和交互能力基本能夠覆蓋常見的業務需求。須要快速開發的狀況下G6確實是個不錯的選擇。