基於 HTML5 Canvas 繪製的電信網路拓撲圖

圖撲軟體發表於2018-02-07

電信網結構(telecommunication network structure)是指電信網各種網路單元按技術要求和經濟原則進行組合配置的組合邏輯和配置形式。組合邏輯描述網路功能的體系結構,配置形式描述網路單元的鄰接關係,即以交換中心(或節點)和傳輸鏈路所組成的拓撲結構。常見的網路拓撲結構有星型結構、匯流排結構、環形結構、樹形結構、網狀結構、混合型拓撲以及蜂窩拓撲結構等,本文的例子主要描繪的是匯流排型拓撲,在顯示上相對其他的結構型別來說更清晰明瞭,繪製起來也非常容易。

雖然題目起的名字是電信網路拓撲圖,幾乎所有的拓撲圖都能涵蓋,例如基本網路圖,網路拓撲圖,機架圖,網路通訊圖,3D網路圖等等。

效果圖如下:

圖片描述

這個圖看起來挺簡單的,程式碼也少,但是內容不少。

首先,機櫃01、機櫃02、機櫃03 都是 ht.Group “組”型別,ht.Group 型別用於作為父容器包含孩子圖元,在 GraphView 拓撲圖(http://www.hightopo.com)上可通過雙擊進行展開合併,合併時會自定隱藏子孫圖元節點, 如果有子節點有連線連線到外部時,合併的 Group 將代理進行連線。Group 的移動會帶動孩子節點跟隨, 孩子的位置和大小變化也會影響 Group 的展開圖形和 position 位置。

這邊提到一個代理連線的問題,“代理”兩個字可以很好地表明代理連線意義。實際上就是如果組內部的節點與組外部的節點有連線,那麼在組合並的時候,會在這個組會“代理”與外部節點之間的連線,這個就是代理連線。我們拿機櫃02來說吧,機櫃02內部有一個“電腦”與“內部網路交換機”之間有兩條連線,那麼當我們雙擊機櫃02合併時,實際上就相當於機櫃02與“內部網路交換機”之間有兩條連線了。

那麼,我們來看看如何繪製這個組以及組內部的節點吧,先建立“機櫃02”的 Group 節點,因為整個例子我建立了三個 Group 節點,而且建立的方式都類似,因此把建立組的程式碼封裝起來複用:

function createGroup(name, x, y) {
    var group = new ht.Group();//組型別 實際上也是一個節點
    group.setExpanded(true);//設定展開組
    group.setName(name);//設定組的名字
    group.s({//設定組的樣式style
        'group.title.background': 'rgba(14,36,117,0.80)',//組展開後的title背景顏色,僅對group.type為空的型別起作用
        'group.background': 'rgba(14,36,117,0.40)',//組展開後的背景顏色
        'group.title.align': 'center'//組展開後的title文字水平對齊方式,預設值為'left',可設定為center和right
    });
    group.setPosition(x, y);//設定組的位置
    group.setImage('images/伺服器.json');//設定拓撲上展現的圖片資訊,在GraphView拓撲圖中圖片一般以position為中心繪製
    dataModel.add(group);//將建立的組節點新增進資料容器中

    return group;
}

組是可以通過雙擊展開合併的,展開的時候顯示的是一個有標題欄的框(當然這些都是可以自定義的),合併的時候就顯示上面程式碼中設定的 group.setImage 中的圖片。

所有機櫃內部的節點都是 ht.Node 型別的節點,所以我也封裝了一下:

function createNode(image, parent, x, y) {
    var node = new ht.Node();//建立一個 Node 節點
    if (image) node.setImage(image);//設定節點的顯示圖片
    if (parent) node.setParent(parent);//設定節點的父親
    if (x && y) node.setPosition(x, y);//設定節點的位置
    dataModel.add(node);//將節點新增進資料容器中

    return node;
}

生成機櫃02:

圖片描述

cabinet = createGroup('機櫃02', 146, 445);//建立機櫃02
createNode('images/正常.json', cabinet, 78, 440).s('label', '資料監控分析系統');//建立帶有“正常”圖片的節點,並設定這個節點的文字為“資料監控分析系統”

因為連線需要的是“源節點”以及“終節點”,這邊源節點是中間的“內部網路交換機”,我們再建立這個節點:

var line = createNode();//建立一個節點
line.setSize(725, 20);//設定節點大小
line.setPosition(310, 325);//設定節點位置
line.s({//設定節點的style屬性
    'shape': 'roundRect',//決定shape的形狀,預設值為空,代表用image繪製。roundRect四周圓角矩形
    'shape.background': 'rgba(14,36,117,0.80)',//背景填充顏色,為null代表不填充背景
    'shape.border.color': '#979797',//邊框顏色
    'shape.corner.radius': 10,//該引數指定roundRect型別的圓角半徑,預設為空系統自動調節,可設定正數值
    'label': '內部網路交換機', //文字內容,預設為空
    'label.position': 45,//文字內容,預設為空
    'label.offset.x': 50,//文字水平偏移,對於Edge意味著沿著連線方向水平偏移
    'label2': '內部網路交換機',//HT預設除了label.*的屬性外,還提供了label2.*的屬性,用於滿足一個圖元需要顯示雙文字的情況
    'label2.position': 48,
    'label2.offset.x': 50,
    'label2.offset.y': 2,
});

不知道你們有沒有注意到,有一個 label2 的樣式屬性,這個是 HT 為了能在一個節點上新增兩個 label 文字而增加的功能,label 屬性和 label2 的屬性是完全相同的,只要在設定屬性的時候用 label 和 label2 區分開來就可以。

源節點和終節點都具備了,可以製作連線了:

createEdge(line, createNode('images/電腦.json', cabinet, 185, 450), 'rgb(30,232,178)', -100, true);
//引數1 源節點,引數2 終節點,引數3 連線顏色,引數4 連線起始點的水平偏移,引數5 是否建立兩條連線

還有一點有趣的,“交換機”的部分,最左側藍色方形的節點和中間長條的節點並不是一體的,而是分離的,但是我通過 setHost 進行節點與節點間的吸附,然後反吸附回來,這樣操作上就相當於這兩個節點是一體的:

var exchange = createNode('images/交換機.json', null, -53, 313);
exchange.setHost(line);//設定吸附
line.setHost(exchange);//反吸附 又設定line的吸附為exchange

因為 HT 會按照節點新增進資料容器中的順序來進行層次的排列,我的交換機是在 line 的新增之後的,所以預設交換機的節點會顯示在 line 之下,我們將預設的層級顯示關閉,並設定交換機 exchange 顯示在資料容器的頂部:

dataModel.setAutoAdjustIndex(false);//將自動調整data在容器中索引順序的開關關閉
dataModel.sendToTop(exchange);//將data在拓撲上置頂

程式碼就是這些。還有不懂的可以留言或者私信我也可以,大家可以一起探討。

相關文章