一、實現的功能
1、拓撲的基本繪製
2、拓撲放射性佈局
3、多級拓撲的實現
4、下級拓撲節點的隱藏與展現
5、導出拓撲圖
二、效果圖展示
正常展示:
節點收縮展示:
三、引入庫(稍作可以直接複用)
1、引入jQuery,jquery.js
2、引入jtopo插件jtopo-0.4.8.js
3、支持IE8,excanvas.js
四、前端代碼
在實現節點隱藏於展示過程中,實現方法可能不夠好,問題如下:
1、而且使用全局數組(用來存儲隱藏的節點),重新展示後全局數組裏面的數據無法刪除,因爲不敢保證所有節點同時展示與收縮,因此使用的是當重新展示後,把重新展示的節點對象用對應的數字替換數組中的位置,這樣保證再次隱藏的時候數組能夠重新添加。有的人可能會問既然可能會第二次隱藏,那爲什麼要把存在數組中的對應的展示的節點對象刪除呢?那是爲了防止當重複再節點上添加相同的節點。(改進思路,用拓撲對象的outLinks屬性作爲增刪的校驗)
2、判斷節點點擊展示或隱藏的方法待改進,我採用的是數字計數法,缺陷就是一次或兩次有效(改進思路,暫無)
3、js代碼:
<script type="text/javascript">
var ip = '10.0.1.254';
var id = <%=request.getParameter("id")%>; //
var array = new Array();
var count = 1;
$(document).ready(function(){
$.ajax({
type:'post',
url:'${basePath}JtopoController/JtopoNew.do?ip='+ip+'&id='+1+"&isInter=yes",
dataType:"json",
async:false,
success:function (obj){
console.info(obj);
nodes=eval(obj);
console.info(nodes);
var canvas =document.getElementById('canvas');
//初始化畫布
var stage;
}
});
initScene();
});
function createCloudNode(){ //繪製核心拓撲
var cloudNode = new JTopo.Node();
cloudNode.setSize(39,36);
cloudNode.setImage("jiaohuanji.png");
cloudNode.setLocation(1000,1000);
cloudNode.text=ip;
cloudNode.font= "20px Consolas";
cloudNode.fontColor = "555,555,0";
return cloudNode;
}
function initScene(){//初始化場景
stage=new JTopo.Stage(canvas);
//顯示工具欄
//showJTopoToobar(stage);click
scene =new JTopo.Scene(stage);
scene.alpha=1;
scene.backgroundColor="#C8C8C8";
var cloudNode;
cloudNode=createCloudNode();
scene.add(cloudNode);
drawTopo(nodes,cloudNode);
scene.addEventListener("click", eventHandler);
}
function compare(vv){ //掃描當前點擊拓撲是否在數組中
var strV = vv.text.substring(0,vv.text.lastIndexOf("."))+".";
var str = "";
var flag = false;
try{
for(var m=0;m<array.length;m++){
str += array[m].text;
}
}catch(e){
}
if(str.contains(strV)){
flag = true;
}
return flag;
}
function eventHandler(){ //jtopo的點擊事件
var node2 = scene.getDisplayedNodes();
var cnode ;
var outlinksCount;
for(var i=1;i<node2.length;i++){
if(node2[i].selected){
outlinksCount = node2[i].outLinks.length;
count ++;
cnode = node2[i];//得到選中的節點
}
}
//如果數組不爲空並且數組中存在該被選擇節點所在網段的元素則恢復
if(array != null&&array.length>=1&&compare(cnode) && count%2==0){
if(outlinksCount == 0){
//alert("add");
//var node = window.sessionStorage.getItem("cloudenodetext");
//window.sessionStorage.removeItem("cloudenodetext");
for(var m=0;m<array.length;m++){
if(array[m] != m){
if(array[m].text.indexOf(cnode.text.substring(0,cnode.text.lastIndexOf("."))+".")!=-1){
//添加之前,需要先判斷節點上是否有該網段的節點,如果沒有,再添加。
//將數組中存在的添加到節點上
scene.add(array[m]);
scene.add(new JTopo.Link(cnode, array[m]));
//每次增加後需要刪除該元素
array[m] = m;
}
}
}
}
}else{//如果爲空或者被數組中不存在被選中的節點所在的網段的節點則進行刪除
for(var i=1;i<node2.length;i++){
if(node2[i].selected){
if(outlinksCount > 0){
for(var j=1;j<node2.length;j++){
if(node2[j].text.contains(node2[i].text.substring(0,node2[i].text.lastIndexOf("."))+".")){
if(node2[j]!=node2[i] && node2[j]!=null && node2[j]!=undefined && node2[j]!=""){
if(isNotInArr(node2[j].text)){
array.push(node2[j]);
}
scene.remove(node2[j]);
window.sessionStorage.setItem("cloudenodetext",node2[i].text);
}
}
}
}
}
}
}
}
//判斷是否在數組中
function isNotInArr(node){
//alert(".....is not node ");
var str = "";
var flag = false;
if (array==null||array.length==0)
return true;
for(var i=0;i<array.length;i++){
str += array[i].text;
}
if(!str.contains(node)){
//alert("in here。。");
flag = true;
}
return flag;
}
function addNode(name,x,y,cloudNode){ //將生產的單個拓撲加入到scence中
var node = new JTopo.Node();
node.setSize(7, 7);
node.setLocation(x,y);
node.setImage("tp_node.png");
node.text=name;
dragable:true;
node.fontColor = "219,118,39";
scene.add(node);
var link = new JTopo.Link(cloudNode,node);
link.strokeColor = "128,64,64";
scene.add(link);
return node;
}
function drawTopo(nodes,cloudNode){ // 繪製拓撲
for(var i=0;i<nodes.nodelist.length;i++){
var nd = addNode(nodes.nodelist[i].name,nodes.nodelist[i].pt.x,nodes.nodelist[i].pt.y,cloudNode);
JTopo.layout.layoutNode(scene,cloudNode,true);
drawTopo(nodes.nodelist[i],nd);
}
}
function exportImage(){ //導出圖片
stage.saveImageInfo();
}
</script>
HTML:
<body >
<div>
<div id="lookAll" style="height:25px;float: left"><button onclick="exportImage();">導出成圖片</button></div>
<div id="content" style="width:1000px;">
<canvas width="3000" height="3000" id="canvas" style="border:1px solid rgb(68,68,68); cursor:default;background-color:argb(338,338,338)"></canvas>
</div>
</div>
</body>
4、json格式:
{
"crlr": 5,
"degree": 360,
"disitem": 360,
"name": "asffa",
"nodelist": [
{
"crlr": 5,
"degree": 120,
"disitem": null,
"name": "210.27.48.30",
"nodelist": [],
"noder": null,
"pt": {
"x": 6.12421475744837E+16,
"y": 1000
},
"rt": {
"center": {
"x": 1000,
"y": 1000
},
"degree": 0,
"r": 6.12421475744827E+16
},
"xdegree": -60
}
],
"noder": 6.12421475744827E+16,
"pt": {
"x": 1000,
"y": 1000
},
"xdegree": 0
}
5、後臺代碼,拓撲對象座標計算,使用的是弧度定位算法:
public class JtopNode {
public String name = "";
public class toppoint
{
public double x;
public double y;
/*public toppoint(double x,double y){
this.x = x;
this.y = y;
}*/
}
class toprpt
{
public double r;
public double degree;
public toppoint center = new toppoint();
}
public class topnode
{
public List<topnode> nodelist = new ArrayList<JtopNode.topnode>();
public topnode parentnode ;
public String name = "asffa";
public toprpt rt;//圓形座標
public toppoint pt;//座標
public double crlr = 5;//小球半徑
public double noder;//扇面半徑
public double disitem;//扇形間隔角
public double degree;//扇面角度
public double xdegree;//扇形底線夾角
}
toppoint rt2pt(toprpt rt)
{
double y1 = rt.r * Math.sin(rt.degree *Math.PI / 180);
double x1 = rt.r * Math.cos(rt.degree * Math.PI / 180);
toppoint tp = new toppoint();
tp.x = rt.center.x+x1;
tp.y = rt.center.y+y1;
return tp;
}
void getnode_parm(topnode nd)
{
double disitem = nd.degree / (nd.nodelist.size());
double minr = nd.crlr / Math.sin(0.5 * disitem * Math.PI / 180);
nd.disitem = disitem;
nd.noder = minr * 1.5 + nd.crlr * 3+80;
if (nd.noder < 250)
{
nd.noder = 250;
}
}
//得到直角座標
void getchildnode_pt(topnode nd)
{
if(nd.nodelist==null&&nd.nodelist.size()<1) return;
for (int i = 0; i < nd.nodelist.size(); i++)
{
nd.nodelist.get(i).degree = 120.0;
toprpt rt = new toprpt();
rt.center = new toppoint();
rt.center.x = nd.pt.x;
rt.center.y = nd.pt.y;
rt.r = nd.noder;
rt.degree = nd.xdegree + nd.disitem * i;
nd.nodelist.get(i).xdegree = rt.degree - nd.nodelist.get(i).degree / 2;// (rt.degree - nd.nodelist[i].degree / 2);
nd.nodelist.get(i).rt = rt;
nd.nodelist.get(i).pt = new toppoint();// = rt2pt(rt);
toppoint tp = rt2pt(rt);
nd.nodelist.get(i).pt.x = tp.x;
nd.nodelist.get(i).pt.y = tp.y;
getnode_parm(nd.nodelist.get(i));
getchildnode_pt(nd.nodelist.get(i));
}
}
public void setrootnode_parm(topnode root)
{
root.degree = 360;
root.xdegree = 0;
root.pt = new toppoint();
root.pt.x=1000.0;
root.pt.y=1000.0;
getnode_parm(root);
// drawitem(root, root.pt);
getchildnode_pt(root);
}
public void parseXML(NmTopoRelation nmTopoRelation,TopoResult topoResult){
System.out.println("topoID="+nmTopoRelation.getDevtypeId());
System.out.println("topoIp="+nmTopoRelation.getDevIp());
//創建XML解析對象
xml x = new xml();
//初始化,解析XML,得到子節點
List<NmTopoRelation> interIpList = x.xmlRouters(nmTopoRelation.getDevtypeId());
//得到子網所對應的所有IP
List<NmTopoRelation> childLists = x.xmlSubnets();
String subIp = "";
List<String> ipList = new ArrayList<String>();
//獲取所有二層節點IP
for(NmTopoRelation parent:interIpList){
if(parent.getDevIp() != null){
ipList.add(parent.getDevIp());
}
}
//核心ip就是路由ip,根據傳進來的路由ip得到它所對應的所有子網ip,然後根據子網ip進行繪製
//因爲是子網,所有主機號爲0,因此將核心ip的主機號剪掉,得到一個網絡號,再利用網絡號與掃描的ip進行匹配
//對掃描到的ip進行網段匹配
if(interIpList != null&&childLists != null){
if(ipList.contains(nmTopoRelation.getDevIp()))
{
List<topnode> tops = nmtp2Td(interIpList);
for(topnode td:tops){
if(td.name.equals(nmTopoRelation.getDevIp())){
continue;
}
subIp = td.name.substring(0, td.name.lastIndexOf("."))+".";
List<topnode> tops2 = nmtp2Td(childLists);
for(topnode td2:tops2){
if(td2.name.indexOf(subIp)<0){
continue;
}
if(td.name.equals(td2.name)){
continue;
}
td.nodelist.add(td2);
}
topoResult.getTnode().nodelist.add(td);
}
}
}
setrootnode_parm(topoResult.getTnode());
}
public List<topnode> nmtp2Td(List<NmTopoRelation> nmlist){
List<topnode> tdlist = new ArrayList<topnode>();
topnode td = null;
for(int i = 0; i < nmlist.size();i ++){
td = new topnode();
td.name = nmlist.get(i).getDevIp();
tdlist.add(td);
}
return tdlist;
}
}
6、以上後臺代碼只是實現了三層拓撲圖,如果還有更多層,需要對這個方法 nmtp2Td() 稍作修改
接下來直接使用fastjson格式化一把傳入js就搞定。祝你愉快!