一、实现的功能
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就搞定。祝你愉快!