使用JsPlumb繪制拓撲圖的通用方法


轉自:http://www.it165.net/pro/html/201311/7616.html

使用JsPlumb繪制拓撲圖的通用方法

一、 實現目標

繪制拓撲圖, 實際上是個數據結構和算法的問題。 需要設計一個合適的數據結構來表達拓撲結構,設計一個算法來計算拓撲節點的位置及連接。

\
 

二、 實現思想

1. 數據結構

首先, 從節點開始。 顯然, 需要一個字段 type 表示節點類型, 一個字段 data 表示節點數據(詳情), 對於連接, 則采用一個 rel 字段, 表示有哪些節點與之關聯, 相當於C 里面的指針。 為了唯一標識該節點, 還需要一個字段 key 。 通過 type-key 組合來唯一標識該節點。 這樣, 初步定下數據結構如下:

a. 節點數據結構: node = { type: 'typeName', key: 'key', rel: [], data: {'More Info'}}
b. rel, data 可選 , type-key 唯一標識該節點, rel 為空標識該節點為葉子節點
c. 關聯關系: rel: [node1, node2, ..., nodeN]
d. 更多詳情: 關於節點的更多信息可放置於此屬性中

2. 算法

在算法上, 要預先規划好各個節點類型如何布局以及如何連接。 連接方向很容易定: 根據起始節點及終止節點的類型組合, 可以規定不同的連接方向。 位置確定稍有點麻煩。 這里采用的方法是: 采用深度遍歷方法, 下一個的節點位置通過上一個節點位置確定, 不同類型的節點位置計算不一樣, 但是相同類型的節點位置是重合的, 需要在后面進行調整。實際上, 這個節點位置的算法是不夠高明的, 如果有更好的算法, 請告知。

3. JsPlumb

jsPlumb 有幾個基本概念。 首先, 拓撲節點實際上是 DIV 區域,每個DIV 都必須有一個ID,用於唯一標識該節點。 連接拓撲節點的一個重要概念是EndPoint . EndPoint 是附着於節點上的連接線的端點, 簡稱“附着點”。 將附着點 attach 到指定拓撲節點上的方法如下:

jsPlumb.addEndpoint(toId, this.sourceEndpoint, { anchor: sourceAnchor, uuid:sourceUUID });

toId 是 拓撲節點的 DIV 區域的 ID 值, sourceEndpoint 是附着點的樣式設置, 可以復用 , sourceAnchor 是附着點位置, 共有八種:

Top (also aliased as TopCenter) - TopRight - Right (also aliased as RightMiddle) - BottomRight - Bottom (also aliased asBottomCenter) -BottomLeft - Left (also aliased as LeftMiddle) - TopLeft

sourceUUID 是拓撲節點與附着位置的結合, 也就是說, 要將一個 附着點附着到拓撲節點為 toId 的 sourceAnchor 指定的位置上。 每個拓撲節點都可以定義多個源附着點和目標附着點。 源附着點是連接線的起始端, 目標附着點是連接線的終止端。

兩個 uuid 即可定義一條連接線:

jsPlumb.connect({uuids:[startPoint, endPoint], editable: false});

startPoint 和 endPoint 分別是連接線的起始端 Endpoint uuid 和 終止段 Endpoint uuid. 它定義了從起始拓撲節點的指定附着點連接到終止拓撲節點的指定附着點。

 

三、 實現代碼

drawTopo.js 提供繪制拓撲圖的基本方法, 只要按照數據結構扔進去, 就可以自動繪制出拓撲圖來。

  1 /**
  2  * 使用 jsPlumb 根據指定的拓撲數據結構繪制拓撲圖
  3  * 使用 drawTopo(topoData, nodeTypeArray) 方法
  4  * 
  5  */
  6 
  7 /**
  8  * 初始化拓撲圖實例及外觀設置
  9  */
 10 (function() {
 11     
 12     jsPlumb.importDefaults({
 13         
 14         DragOptions : { cursor: 'pointer', zIndex:2000 },
 15     
 16         EndpointStyles : [{ fillStyle:'#225588' }, { fillStyle:'#558822' }],
 17     
 18         Endpoints : [ [ "Dot", { radius:2 } ], [ "Dot", { radius: 2 } ]],
 19     
 20         ConnectionOverlays : [
 21             [ "Arrow", { location:1 } ],
 22             [ "Label", { 
 23                 location:0.1,
 24                 id:"label",
 25                 cssClass:"aLabel"
 26             }]
 27         ]
 28     });
 29     
 30     var connectorPaintStyle = {
 31         lineWidth: 1,
 32         strokeStyle: "#096EBB",
 33         joinstyle:"round",
 34         outlineColor: "#096EBB",
 35         outlineWidth: 1
 36     };
 37     
 38     var connectorHoverStyle = {
 39         lineWidth: 2,
 40         strokeStyle: "#5C96BC",
 41         outlineWidth: 2,
 42         outlineColor:"white"
 43     };
 44     
 45     var endpointHoverStyle = {
 46         fillStyle:"#5C96BC"
 47     };
 48     
 49     window.topoDrawUtil = {
 50             
 51         sourceEndpoint: {
 52             endpoint:"Dot",
 53             paintStyle:{ 
 54                 strokeStyle:"#1e8151",
 55                 fillStyle:"transparent",
 56                 radius: 2,
 57                 lineWidth:2 
 58             },                
 59             isSource:true,
 60             maxConnections:-1,
 61             connector:[ "Flowchart", { stub:[40, 60], gap:10, cornerRadius:5, alwaysRespectStubs:true } ],                                                
 62             connectorStyle: connectorPaintStyle,
 63             hoverPaintStyle: endpointHoverStyle,
 64             connectorHoverStyle: connectorHoverStyle,
 65             dragOptions:{},
 66             overlays:[
 67                 [ "Label", { 
 68                     location:[0.5, 1.5], 
 69                     label:"",
 70                     cssClass:"endpointSourceLabel" 
 71                 } ]
 72             ]
 73         },
 74         
 75         targetEndpoint: {
 76             endpoint: "Dot",                    
 77             paintStyle: { fillStyle:"#1e8151",radius: 2 },
 78             hoverPaintStyle: endpointHoverStyle,
 79             maxConnections:-1,
 80             dropOptions:{ hoverClass:"hover", activeClass:"active" },
 81             isTarget:true,            
 82             overlays:[
 83                 [ "Label", { location:[0.5, -0.5], label:"", cssClass:"endpointTargetLabel" } ]
 84             ]
 85         },
 86         
 87         initConnection: function(connection) {
 88             connection.getOverlay("label").setLabel(connection.sourceId + "-" + connection.targetId);
 89             connection.bind("editCompleted", function(o) {
 90                 if (typeof console != "undefined")
 91                     console.log("connection edited. path is now ", o.path);
 92             });
 93         },            
 94 
 95         addEndpoints: function(toId, sourceAnchors, targetAnchors) {
 96             for (var i = 0; i < sourceAnchors.length; i++) {
 97                 var sourceUUID = toId + sourceAnchors[i];
 98                 jsPlumb.addEndpoint(toId, this.sourceEndpoint, { anchor:sourceAnchors[i], uuid:sourceUUID });                        
 99             }
100             for (var j = 0; j < targetAnchors.length; j++) {
101                 var targetUUID = toId + targetAnchors[j];
102                 jsPlumb.addEndpoint(toId, this.targetEndpoint, { anchor:targetAnchors[j], uuid:targetUUID });                        
103             }
104         }
105     };
106 
107 
108 })();
109 
110 /**
111  * drawTopo 根據給定拓撲數據繪制拓撲圖
112  * @param topoData 拓撲數據
113  * @param rootPosition 拓撲圖根節點的位置
114  * @param nodeTypeArray 節點類型數組
115  * 
116  * 拓撲圖的所有節點是自動生成的, DIV class = "node" , id= nodeType.toUpperCase + "-" + key 
117  * 拓撲圖的所有節點連接也是自動生成的, 可以進行算法改善與優化, 但使用者不需要關心此問題
118  * 需要定義節點類型數組 nodeTypeArray
119  * 
120  * 拓撲數據結構:
121  * 1. 節點數據結構: node = { type: 'typeName', key: 'key', rel: [], data: {'More Info'}} 
122  *    rel, data 可選 , type-key 唯一標識該節點
123  * 2. 關聯關系: rel: [node1, node2, ..., nodeN]
124  * 3. 更多詳情: 關於節點的更多信息可放置於此屬性中
125  * 4. 示例:
126  *   var topoData = {
127  *            type: 'VM', key: '110.75.188.35', 
128  *            rel: [
129  *                 {   type: 'DEVICE', key: '3-120343' },
130  *                 {      type: 'DEVICE', key: '3-120344' },
131  *                 {      type: 'VIP',       key: '223.6.250.2',
132  *                     rel: [
133  *                         { type: 'VM', key: '110.75.189.12' },
134  *                         { type: 'VM', key: '110.75.189.12' }
135  *                     ]
136  *                 },
137  *                 {      type: 'NC',  key: '10.242.192.2',
138  *                     rel: [
139  *                       { type: 'VM', key: '110.75.188.132' },
140  *                       { type: 'VM', key: '110.75.188.135' },
141  *                       { type: 'VM', key: '110.75.188.140' }
142  *                     ]
143  *                 
144  *                 }
145  *            ]
146  *        };
147  * 
148  */
149 function drawTopo(topoData, rootPosition, nodeTypeArray) {
150     
151     // 創建所有拓撲節點及連接並確定其位置
152     createNodes(topoData, rootPosition, nodeTypeArray);
153     
154     // 調整重合節點的位置, 添加節點的附着點, 即連接線的端點
155     adjust(topoData, nodeTypeArray);
156     
157     // 使所有拓撲節點均為可拉拽的                    
158     jsPlumb.draggable(jsPlumb.getSelector(".node"), { grid: [5, 5] });
159     
160     // 創建所有節點連接
161     createConnections(topoData, nodeTypeArray);
162     
163 }
164 
165 /**
166  * 根據給定拓撲數據繪制拓撲節點並確定其位置, 使用深度優先遍歷
167  * @param topoData 拓撲數據
168  * @param rootPosition 根節點的位置設定
169  * @param nodeTypeArray 拓撲節點類型
170  */
171 function createNodes(rootData, rootPosition, nodeTypeArray) {
172     
173     if (rootData == null) {
174         return ;
175     }
176     
177     var topoRegion = $('#topoRegion');
178     var relData = rootData.rel;
179     var i=0, relLen = relLength(relData);;
180     var VM_TYPE = nodeTypeArray[0];
181     var DEVICE_TYPE = nodeTypeArray[1];
182     var NC_TYPE = nodeTypeArray[2];
183     var VIP_TYPE = nodeTypeArray[3];
184     
185     // 根節點的位置, 單位: px
186     var rootTop = rootPosition[0];
187     var rootLeft = rootPosition[1];
188     
189     var nextRootData = {};
190     var nextRootPosition = [];
191     
192     // 自動生成並插入根節點的 DIV 
193     var divStr = createDiv(rootData);
194     var nodeDivId = obtainNodeDivId(rootData);
195     topoRegion.append(divStr);
196     //console.log(divStr);
197     
198     // 設置節點位置
199     $('#'+nodeDivId).css('top', rootTop + 'px');
200     $('#'+nodeDivId).css('left', rootLeft + 'px');
201     
202     for (i=0; i < relLen; i++) {
203         nextRootData = relData[i];
204         nextRootPosition = obtainNextRootPosition(rootData, nextRootData, rootPosition, nodeTypeArray);
205         createNodes(nextRootData, nextRootPosition, nodeTypeArray);
206     }
207 
208 }
209 
210 /**
211  * 調整重合節點的位置, 並添加節點的附着點, 即連接線的端點
212  */
213 function adjust(topoData, nodeTypeArray) {
214     
215     var vm_deviceOffset = 0;   // 起始節點為 vm , 終止節點為 device, device div 的偏移量
216     var vm_vipOffset = 0;      // 起始節點為 vm , 終止節點為 vip, vip div 的偏移量
217     var vm_ncOffset = 0;       // 起始節點為 vm , 終止節點為 nc, nc div 的偏移量 
218     var vip_vmOffset = 0;      // 起始節點為 vip , 終止節點為 vm, vm div 的偏移量     
219     var nc_vmOffset = 0;      // 起始節點為nc , 終止節點為 vm, vm div 的偏移量 
220     var verticalDistance = 120;
221     var horizontalDistance = 150;
222     
223     var VM_TYPE = nodeTypeArray[0];
224     var DEVICE_TYPE = nodeTypeArray[1];
225     var NC_TYPE = nodeTypeArray[2];
226     var VIP_TYPE = nodeTypeArray[3];
227     
228     $('.node').each(function(index, element) {
229         var nodeDivId = $(element).attr('id');
230         var nodeType = nodeDivId.split('-')[0];
231         var offset = $(element).offset();
232         var originalTop = offset.top;
233         var originalLeft = offset.left;
234         var parentNode = $(element).parent();
235         var parentNodeType = parentNode.attr('id').split('-')[0];
236         switch (nodeType) {
237             case VM_TYPE:
238                 // VM 位置水平偏移
239                 $(element).css('left', (originalLeft + vip_vmOffset*horizontalDistance) + 'px');
240                 vip_vmOffset++;
241                 topoDrawUtil.addEndpoints(nodeDivId, ['Top', 'Bottom', 'Right'], []);
242                 break;
243             case DEVICE_TYPE:
244                 // DEVICE 位置垂直偏移
245                 $(element).css('top', (originalTop + (vm_deviceOffset-1)*verticalDistance) + 'px');
246                 vm_deviceOffset++;
247                 topoDrawUtil.addEndpoints(nodeDivId, [], ['Left']);
248                 break;
249             case VIP_TYPE:
250                 // VIP 位置水平偏移
251                 $(element).css('left', (originalLeft + vm_vipOffset*horizontalDistance) + 'px');
252                 vm_vipOffset++;
253                 topoDrawUtil.addEndpoints(nodeDivId, ['Top'], ['Bottom']);
254                 break;
255             case NC_TYPE:
256                 // NC 位置水平偏移
257                 $(element).css('left', (originalLeft + vm_ncOffset*verticalDistance) + 'px');
258                 vm_ncOffset++;
259                 topoDrawUtil.addEndpoints(nodeDivId, ['Bottom'], ['Top']);
260                 break;
261             default:
262                 break;
263         }
264     });
265 }
266 
267 /**
268  * 獲取下一個根節點的位置, 若節點類型相同, 則位置會重合, 需要后續調整一次
269  * @root            當前根節點
270  * @nextRoot        下一個根節點
271  * @rootPosition    當前根節點的位置
272  * @nodeTypeArray   節點類型數組
273  */
274 function obtainNextRootPosition(root, nextRoot, rootPosition, nodeTypeArray) {
275     
276     var VM_TYPE = nodeTypeArray[0];
277     var DEVICE_TYPE = nodeTypeArray[1];
278     var NC_TYPE = nodeTypeArray[2];
279     var VIP_TYPE = nodeTypeArray[3];
280     
281     var startNodeType = root.type;
282     var endNodeType = nextRoot.type;
283     var nextRootPosition = [];
284     var rootTop = rootPosition[0];
285     var rootLeft = rootPosition[1];
286     
287     var verticalDistance = 120;
288     var horizontalDistance = 250;
289     var shortVerticalDistance = 80;
290     
291     switch (startNodeType) {
292         case VM_TYPE:
293             if (endNodeType == VIP_TYPE) {
294                 nextRootPosition = [rootTop-verticalDistance, rootLeft];
295             }
296             else if (endNodeType == DEVICE_TYPE) {
297                 nextRootPosition = [rootTop, rootLeft+horizontalDistance];
298             }
299             else if (endNodeType == NC_TYPE) {
300                 nextRootPosition = [rootTop+verticalDistance, rootLeft];
301             }
302             break;
303         case VIP_TYPE:
304             if (endNodeType == VM_TYPE) {
305                 nextRootPosition = [rootTop-shortVerticalDistance, rootLeft];
306             }
307             break;
308         case NC_TYPE:
309             if (endNodeType == VM_TYPE) {
310                 nextRootPosition = [rootTop+shortVerticalDistance, rootLeft];
311             }
312             break;
313         default: 
314             break;
315     }
316     return nextRootPosition;
317 }
318 
319 /**
320  * 根據給定拓撲數據, 繪制節點之間的連接關系, 使用深度優先遍歷
321  * @param topoData 拓撲數據
322  * @param nodeTypeArray 節點類型數組
323  */
324 function createConnections(topoData, nodeTypeArray) {
325     
326     if (topoData == null) {
327         return ;
328     }
329     var rootData = topoData;
330     var relData = topoData.rel;
331     var i=0, len = relLength(relData);;
332     for (i=0; i < len; i++) {
333         connectionNodes(rootData, relData[i], nodeTypeArray);
334         createConnections(relData[i], nodeTypeArray);
335     }        
336 }
337 
338 /**
339  * 連接起始節點和終止節點
340  * @beginNode 起始節點
341  * @endNode 終止節點
342  * NOTE: 根據是起始節點與終止節點的類型
343  */
344 function connectionNodes(beginNode, endNode, nodeTypeArray) 
345 {
346     var startNodeType = beginNode.type;
347     var endNodeType = endNode.type;
348     var startDirection = '';
349     var endDirection = '';
350     
351     var VM_TYPE = nodeTypeArray[0];
352     var DEVICE_TYPE = nodeTypeArray[1];
353     var NC_TYPE = nodeTypeArray[2];
354     var VIP_TYPE = nodeTypeArray[3];
355     
356     switch (startNodeType) {
357         case VM_TYPE:
358             if (endNodeType == VIP_TYPE) {
359                 // VIP 繪制於 VM 上方
360                 startDirection = 'Top';
361                 endDirection = 'Bottom';
362             }
363             else if (endNodeType == DEVICE_TYPE) {
364                 // DEVICE 繪制於 VM 右方
365                 startDirection = 'Right';
366                 endDirection = 'Left';
367             }
368             else if (endNodeType == NC_TYPE) {
369                 // NC 繪制於 VM 下方
370                 startDirection = 'Bottom';
371                 endDirection = 'Top';
372             }
373             break;
374         case VIP_TYPE:
375             if (endNodeType == VM_TYPE) {
376                 // VM 繪制於 VIP 上方
377                 startDirection = 'Top';
378                 endDirection = 'Top';
379             }
380             break;
381         case NC_TYPE:
382             if (endNodeType == VM_TYPE) {
383                 // VM 繪制於 NC 下方
384                 startDirection = 'Bottom';
385                 endDirection = 'Bottom';
386             }
387             break;
388         default: 
389             break;
390     }
391     var startPoint = obtainNodeDivId(beginNode) + startDirection;
392     var endPoint = obtainNodeDivId(endNode) + endDirection;
393     jsPlumb.connect({uuids:[startPoint, endPoint], editable: false}); 
394 }
395 
396 function createDiv(metaNode) {
397     return '<div class="node" id="' + obtainNodeDivId(metaNode) + '"><strong>' 
398             + metaNode.type + '<br/><a href="http://aliyun.com">' + metaNode.key + '</a><br/></strong></div>'
399 }
400 
401 /**
402  * 生成節點的 DIV id
403  * divId = nodeType.toUpperCase + "-" + key
404  * key 可能為 IP , 其中的 . 將被替換成 ZZZ , 因為 jquery id 選擇器中 . 屬於轉義字符.
405  * eg. {type: 'VM', key: '1.1.1.1' }, divId = 'VM-1ZZZ1ZZZ1ZZZ1'
406  */
407 function obtainNodeDivId(metaNode) {
408     return metaNode.type.toUpperCase() + '-' + transferKey(metaNode.key);
409 }
410 
411 function transferKey(key) {
412     return key.replace(/\./g, 'ZZZ');
413 }
414 
415 function revTransferKey(value) {
416     return value.replace(/ZZZ/g, '.');
417 }
418 
419 
420 /**
421  * 合並新的拓撲結構到原來的拓撲結構中, 新的拓撲結構中有節點與原拓撲結構中的某個節點相匹配: type-key 相等
422  * @param srcTopoData 原來的拓撲結構
423  * @param newTopoData 要添加的的拓撲結構
424  */
425 function mergeNewTopo(srcTopoData, newTopoData) {
426     
427     var srcTopoData = shallowCopyTopo(srcTopoData);
428     
429     if (srcTopoData == null || newTopoData == null) {
430         return srcTopoData || newTopoData;
431     }
432     
433     var srcRoot = srcTopoData;
434     var newRoot = newTopoData;
435 
436     var newRelData = newTopoData.rel;
437     var i=0, newRelLen = relLength(newRelData);
438     
439     var matched = findMatched(srcRoot, newRoot);
440     if (matched == null) {
441         // 沒有找到匹配的節點, 直接返回原有的拓撲結構
442         return srcTopoData;
443     }
444     matched.rel = matched.rel.concat(newRelData);
445     return srcTopoData;
446 }
447 
448 /**
449  * 在原拓撲結構中查找與新拓撲結構根節點 newRootData 匹配的節點
450  * @param srcRootData 原拓撲結構
451  * @param newRootData 新拓撲結構的根節點
452  * @returns 原拓撲結構中與新拓撲結構根節點匹配的節點 or null if not found
453  */
454 function findMatched(srcRootData, newRootData) {
455     var srcRelData = srcRootData.rel;
456     var i=0, srcRelLen = relLength(srcRelData);
457     var matched = null;
458     if ((srcRootData.type == newRootData.type) && (srcRootData.key == newRootData.key)) {
459         return srcRootData;
460     }
461     for (i=0; i<srcRelLen; i++) {
462         matched = findMatched(srcRelData[i], newRootData);
463         if (matched != null) {
464             return matched;
465         }
466     }
467     return matched;
468 }
469 
470 function relLength(relData) {
471     if (isArray(relData)) {
472         return relData.length;
473     } 
474     return 0;
475 }
476 
477 function isArray(value) {
478     return value && (typeof value === 'object') && (typeof value.length === 'number');
479 }
480 
481 /**
482  * 淺復制拓撲結構
483  */
484 function shallowCopyTopo(srcTopoData) {
485     return srcTopoData;
486 }
487 
488 /**
489  * 深復制拓撲結構
490  */
491 function deepCopyTopo(srcTopoData) {
492     //TODO identical to deep copy of js json
493 }

 

topodemo.html 繪制拓撲圖的客戶端接口。 只要引進相應的依賴 JS,預置一個 <div id="topoRegion"></div>

<!doctype html>
<html>
    <head>
        <title>jsPlumb 1.5.3 - flowchart connectors demonstration - jQuery</title>
        <link rel="stylesheet" href="topo-all.css">
        <link rel="stylesheet" href="topo.css">
        
        <!-- DEP -->
        <script src="../jsPlumb/jquery-1.9.0-min.js"></script>
        <script src="../jsPlumb/jquery-ui-1.9.2-min.js"></script>
        
        <!-- /DEP -->
                
        <!-- JS -->
        <!-- support lib for bezier stuff -->
        <script src="../jsPlumb/jsBezier-0.6-min.js"></script> 
        <!-- <a href="http://www.it165.net/pro/webjsp/" target="_blank" class="keylink">jsp</a>lumb geom functions -->   
        <script src="../jsPlumb/<a href="http://www.it165.net/pro/webjsp/" target="_blank" class="keylink">jsp</a>lumb-geom-0.1.js"></script>
        <!-- jsplumb util -->
        <script src="../jsPlumb/util.js"></script>
        <!-- base DOM adapter -->
        <script src="../jsPlumb/dom-adapter.js"></script>        
        <!-- main jsplumb engine -->
        <script src="../jsPlumb/jsPlumb.js"></script>
        <!-- endpoint -->
        <script src="../jsPlumb/endpoint.js"></script>                
        <!-- connection -->
        <script src="../jsPlumb/connection.js"></script>
        <!-- anchors -->
        <script src="../jsPlumb/anchors.js"></script>        
        <!-- connectors, endpoint and overlays  -->
        <script src="../jsPlumb/defaults.js"></script>
        <!-- connector editors -->
        <script src="../jsPlumb/connector-editors.js"></script>
        <!-- bezier connectors -->
        <script src="../jsPlumb/connectors-bezier.js"></script>
        <!-- state machine connectors -->
        <script src="../jsPlumb/connectors-statemachine.js"></script>
        <!-- flowchart connectors -->
        <script src="../jsPlumb/connectors-flowchart.js"></script>        
        <!-- SVG renderer -->
        <script src="../jsPlumb/renderers-svg.js"></script>
        <!-- canvas renderer -->
        <script src="../jsPlumb/renderers-canvas.js"></script>
        <!-- vml renderer -->
        <script src="../jsPlumb/renderers-vml.js"></script>
        
        <!-- jquery jsPlumb adapter -->
        <script src="../jsPlumb/jquery.jsPlumb.js"></script>
        <!-- /JS -->
        
        <!--  demo code -->
        <script src="drawtopo.js"></script>
        
        <script type="text/javascript">
            jsPlumb.bind("ready", function() {
                
                // 拓撲數據結構根節點位置設置
                var rootPosition = [270, 300];
                var nodeTypeArray = ['VM', 'DEVICE', 'NC', 'VIP'];
                var topoData = {
                    type: 'VM', key: '110.75.188.35', 
                    rel: [
                         {
                             type: 'DEVICE',
                             key: '3-120343'
                         },
                        
                         {
                             type: 'DEVICE',
                             key: '3-120344'
                         },
                        
                         {
                             type: 'VIP',
                             key: '223.6.250.2',
                             rel: [
                                 { type: 'VM', key: '110.75.189.12' },
                                 { type: 'VM', key: '110.75.189.13' }
                             ]
                         },
                        
                         {
                             type: 'NC',
                             key: '10.242.192.2',
                             rel: [
                                 { type: 'VM', key: '110.75.188.132' },
                                 { type: 'VM', key: '110.75.188.135' }
                             ]
                         
                         }
                    ]
                };
                
                drawTopo(topoData, rootPosition, nodeTypeArray);
                
                var newTopoData = {
                    type: 'NC',
                    key: '10.242.192.2',
                    rel: [
                           { type: 'VM', key: '110.75.188.140' }
                    ]    
                };
                
                var mergedTopoData = mergeNewTopo(topoData, newTopoData);
                $('#topoRegion').empty();
                drawTopo(mergedTopoData, rootPosition, nodeTypeArray);
            
                
            });
        
        
        </script>
        
    </head>
    <body>        
        
        <div id="topoRegion">
        </div>
            
    </body>
</html>

 

樣式文件及依賴JS 見工程示例。 里面已經包含繪制拓撲圖的最小依賴。

四、 最終效果圖

 

 

源碼下載地址:http://download.csdn.net/detail/shuqin1984/6488513


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM