您的位置:首页 > Web前端 > JavaScript

JsPlumb绘制拓扑图的通用方法

2016-02-17 12:38 531 查看
一、 实现目标

绘制拓扑图, 实际上是个数据结构和算法的问题。 需要设计一个合适的数据结构来表达拓扑结构,设计一个算法来计算拓扑节点的位置及连接。



二、 实现思想

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 as
BottomCenter
)
-
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 提供绘制拓扑图的基本方法, 只要按照数据结构扔进去, 就可以自动绘制出拓扑图来。

view sourceprint?

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>view sourceprint?

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 见工程示例。 里面已经包含绘制拓扑图的最小依赖。

四、 最终效果图



延伸阅读:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: