-
一、 实现目标
绘制拓扑图, 实际上是个数据结构和算法的问题。 需要设计一个合适的数据结构来表达拓扑结构,设计一个算法来计算拓扑节点的位置及连接。
二、 实现思想
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 asTopCenter
) -TopRight
-Right
(also aliased asRightMiddle
) -BottomRight
-Bottom
(also aliased asBottomCenter
) -BottomLeft
-Left
(also aliased asLeftMiddle
) -TopLeft
sourceUUID 是拓扑节点与附着位置的结合, 也就是说, 要将一个 附着点附着到拓扑节点为 toId 的 sourceAnchor 指定的位置上。 每个拓扑节点都可以定义多个源附着点和目标附着点。 源附着点是连接线的起始端, 目标附着点是连接线的终止端。
两个 uuid 即可定义一条连接线:
jsPlumb.connect({uuids:[startPoint, endPoint], editable: false});
startPoint 和 endPoint 分别是连接线的起始端 Endpoint uuid 和 终止段 Endpoint uuid. 它定义了从起始拓扑节点的指定附着点连接到终止拓扑节点的指定附着点。
三、 实现代码
drawTopo.js 提供绘制拓扑图的基本方法, 只要按照数据结构扔进去, 就可以自动绘制出拓扑图来。
001.
/**
002.
* 使用 jsPlumb 根据指定的拓扑数据结构绘制拓扑图
003.
* 使用 drawTopo(topoData, nodeTypeArray) 方法
004.
*
005.
*/
006.
007.
/**
008.
* 初始化拓扑图实例及外观设置
009.
*/
010.
(
function
() {
011.
012.
jsPlumb.importDefaults({
013.
014.
DragOptions : { cursor:
'pointer'
, zIndex:2000 },
015.
016.
EndpointStyles : [{ fillStyle:
'#225588'
}, { fillStyle:
'#558822'
}],
017.
018.
Endpoints : [ [
"Dot"
, { radius:2 } ], [
"Dot"
, { radius: 2 } ]],
019.
020.
ConnectionOverlays : [
021.
[
"Arrow"
, { location:1 } ],
022.
[
"Label"
, {
023.
location:0.1,
024.
id:
"label"
,
025.
cssClass:
"aLabel"
026.
}]
027.
]
028.
});
029.
030.
var
connectorPaintStyle = {
031.
lineWidth: 1,
032.
strokeStyle:
"#096EBB"
,
033.
joinstyle:
"round"
,
034.
outlineColor:
"#096EBB"
,
035.
outlineWidth: 1
036.
};
037.
038.
var
connectorHoverStyle = {
039.
lineWidth: 2,
040.
strokeStyle:
"#5C96BC"
,
041.
outlineWidth: 2,
042.
outlineColor:
"white"
043.
};
044.
045.
var
endpointHoverStyle = {
046.
fillStyle:
"#5C96BC"
047.
};
048.
049.
window.topoDrawUtil = {
050.
051.
sourceEndpoint: {
052.
endpoint:
"Dot"
,
053.
paintStyle:{
054.
strokeStyle:
"#1e8151"
,
055.
fillStyle:
"transparent"
,
056.
radius: 2,
057.
lineWidth:2
058.
},
059.
isSource:
true
,
060.
maxConnections:-1,
061.
connector:[
"Flowchart"
, { stub:[40, 60], gap:10, cornerRadius:5, alwaysRespectStubs:
true
} ],
062.
connectorStyle: connectorPaintStyle,
063.
hoverPaintStyle: endpointHoverStyle,
064.
connectorHoverStyle: connectorHoverStyle,
065.
dragOptions:{},
066.
overlays:[
067.
[
"Label"
, {
068.
location:[0.5, 1.5],
069.
label:
""
,
070.
cssClass:
"endpointSourceLabel"
071.
} ]
072.
]
073.
},
074.
075.
targetEndpoint: {
076.
endpoint:
"Dot"
,
077.
paintStyle: { fillStyle:
"#1e8151"
,radius: 2 },
078.
hoverPaintStyle: endpointHoverStyle,
079.
maxConnections:-1,
080.
dropOptions:{ hoverClass:
"hover"
, activeClass:
"active"
},
081.
isTarget:
true
,
082.
overlays:[
083.
[
"Label"
, { location:[0.5, -0.5], label:
""
, cssClass:
"endpointTargetLabel"
} ]
084.
]
085.
},
086.
087.
initConnection:
function
(connection) {
088.
connection.getOverlay(
"label"
).setLabel(connection.sourceId +
"-"
+ connection.targetId);
089.
connection.bind(
"editCompleted"
,
function
(o) {
090.
if
(
typeof
console !=
"undefined"
)
091.
console.log(
"connection edited. path is now "
, o.path);
092.
});
093.
},
094.
095.
addEndpoints:
function
(toId, sourceAnchors, targetAnchors) {
096.
for
(
var
i = 0; i < sourceAnchors.length; i++) {
097.
var
sourceUUID = toId + sourceAnchors[i];
098.
jsPlumb.addEndpoint(toId,
this
.sourceEndpoint, { anchor:sourceAnchors[i], uuid:sourceUUID });
099.
}
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>
001.
<!doctype html>
002.
<
html
>
003.
<
head
>
004.
<
title
>jsPlumb 1.5.3 - flowchart connectors demonstration - jQuery</
title
>
005.
<
link
rel
=
"stylesheet"
href
=
"topo-all.css"
>
006.
<
link
rel
=
"stylesheet"
href
=
"topo.css"
>
007.
008.
<!-- DEP -->
009.
<
script
src
=
"../jsPlumb/jquery-1.9.0-min.js"
></
script
>
010.
<
script
src
=
"../jsPlumb/jquery-ui-1.9.2-min.js"
></
script
>
011.
012.
<!-- /DEP -->
013.
014.
<!-- JS -->
015.
<!-- support lib for bezier stuff -->
016.
<
script
src
=
"../jsPlumb/jsBezier-0.6-min.js"
></
script
>
017.
<!-- <a href="http://www.it165.net/pro/webjsp/" target="_blank" class="keylink">jsp</a>lumb geom functions -->
018.
<
script
src
=
"../jsPlumb/<a href="
http://www.it165.net/pro/webjsp/"
target
=
"_blank"
class
=
"keylink"
>jsp</
a
>lumb-geom-0.1.js"></
script
>
019.
<!-- jsplumb util -->
020.
<
script
src
=
"../jsPlumb/util.js"
></
script
>
021.
<!-- base DOM adapter -->
022.
<
script
src
=
"../jsPlumb/dom-adapter.js"
></
script
>
023.
<!-- main jsplumb engine -->
024.
<
script
src
=
"../jsPlumb/jsPlumb.js"
></
script
>
025.
<!-- endpoint -->
026.
<
script
src
=
"../jsPlumb/endpoint.js"
></
script
>
027.
<!-- connection -->
028.
<
script
src
=
"../jsPlumb/connection.js"
></
script
>
029.
<!-- anchors -->
030.
<
script
src
=
"../jsPlumb/anchors.js"
></
script
>
031.
<!-- connectors, endpoint and overlays -->
032.
<
script
src
=
"../jsPlumb/defaults.js"
></
script
>
033.
<!-- connector editors -->
034.
<
script
src
=
"../jsPlumb/connector-editors.js"
></
script
>
035.
<!-- bezier connectors -->
036.
<
script
src
=
"../jsPlumb/connectors-bezier.js"
></
script
>
037.
<!-- state machine connectors -->
038.
<
script
src
=
"../jsPlumb/connectors-statemachine.js"
></
script
>
039.
<!-- flowchart connectors -->
040.
<
script
src
=
"../jsPlumb/connectors-flowchart.js"
></
script
>
041.
<!-- SVG renderer -->
042.
<
script
src
=
"../jsPlumb/renderers-svg.js"
></
script
>
043.
<!-- canvas renderer -->
044.
<
script
src
=
"../jsPlumb/renderers-canvas.js"
></
script
>
045.
<!-- vml renderer -->
046.
<
script
src
=
"../jsPlumb/renderers-vml.js"
></
script
>
047.
048.
<!-- jquery jsPlumb adapter -->
049.
<
script
src
=
"../jsPlumb/jquery.jsPlumb.js"
></
script
>
050.
<!-- /JS -->
051.
052.
<!-- demo code -->
053.
<
script
src
=
"drawtopo.js"
></
script
>
054.
055.
<
script
type
=
"text/javascript"
>
056.
jsPlumb.bind("ready", function() {
057.
058.
// 拓扑数据结构根节点位置设置
059.
var rootPosition = [270, 300];
060.
var nodeTypeArray = ['VM', 'DEVICE', 'NC', 'VIP'];
061.
var topoData = {
062.
type: 'VM', key: '110.75.188.35',
063.
rel: [
064.
{
065.
type: 'DEVICE',
066.
key: '3-120343'
067.
},
068.
069.
{
070.
type: 'DEVICE',
071.
key: '3-120344'
072.
},
073.
074.
{
075.
type: 'VIP',
076.
key: '223.6.250.2',
077.
rel: [
078.
{ type: 'VM', key: '110.75.189.12' },
079.
{ type: 'VM', key: '110.75.189.13' }
080.
]
081.
},
082.
083.
{
084.
type: 'NC',
085.
key: '10.242.192.2',
086.
rel: [
087.
{ type: 'VM', key: '110.75.188.132' },
088.
{ type: 'VM', key: '110.75.188.135' }
089.
]
090.
091.
}
092.
]
093.
};
094.
095.
drawTopo(topoData, rootPosition, nodeTypeArray);
096.
097.
var newTopoData = {
098.
type: 'NC',
099.
key: '10.242.192.2',
100.
rel: [
101.
{ type: 'VM', key: '110.75.188.140' }
102.
]
103.
};
104.
105.
var mergedTopoData = mergeNewTopo(topoData, newTopoData);
106.
$('#topoRegion').empty();
107.
drawTopo(mergedTopoData, rootPosition, nodeTypeArray);
108.
109.
110.
});
111.
112.
113.
</
script
>
114.
115.
</
head
>
116.
<
body
>
117.
118.
<
div
id
=
"topoRegion"
>
119.
</
div
>
120.
121.
</
body
>
122.
</
html
>
样式文件及依赖JS 见工程示例。 里面已经包含绘制拓扑图的最小依赖。
四、 最终效果图
JsPlumb绘制拓扑图的通用方法
最新推荐文章于 2024-07-10 10:40:35 发布