jtopo 摺疊與展開子節點

jtopo 摺疊與展開子節點

通過對象記錄狀態,簡單實現功能:

var foldOpenStatus = {}; 			// 記錄摺疊狀態
function foldOpen(e){ 	 			// 摺疊展開
	var thisNode = e.target.text;  	// 第一層以當前節點名稱爲 key 區分摺疊狀態
	var tarlink = e.target.outLinks;

	if(tarlink == undefined){
		return
	}

	if(tarlink.length != 0 && tarlink[0].visible === true){
		var status = [];
		for (var i = 0; i < tarlink.length; i++){
			status[i] = {node: tarlink[i].nodeZ.visible, link: tarlink[i].visible};
			foldOpenStatus[thisNode] = status;
			tarlink[i].nodeZ.visible = false;
			tarlink[i].visible = false;
			
			// 下一層還有節點
			if( tarlink[i].nodeZ.outLinks.length != 0){ 
				dbfold(tarlink[i].nodeZ.outLinks, foldOpenStatus[thisNode][i]);
			}
		}
	}else if(tarlink.length != 0 && tarlink[0].visible === false){
		for (var k = 0; k < tarlink.length; k++){
			tarlink[k].nodeZ.visible =  foldOpenStatus[thisNode][k].node;
			tarlink[k].visible = foldOpenStatus[thisNode][k].link;

			 // 下一層還有節點
			if( tarlink[k].nodeZ.outLinks.length != 0){
				dbOpen(tarlink[k].nodeZ.outLinks, foldOpenStatus[thisNode][k]);
			}
		}
	}

	function dbfold(dblink,foldStatus){
		var status = [];  // 下層以 status 爲 key 記錄
		for(var j = 0; j < dblink.length; j++){
			status[j] = {node: dblink[j].nodeZ.visible, link: dblink[j].visible};
			foldStatus.status = status;
			dblink[j].nodeZ.visible = false;
			dblink[j].visible = false;

			if( dblink[j].nodeZ.outLinks.length != 0){ 
				dbfold(dblink[j].nodeZ.outLinks, foldStatus.status[j]);
			}
		}
	}
	function dbOpen(dblink, openStatus){
		for(var j = 0; j < dblink.length; j++){
			dblink[j].nodeZ.visible = openStatus.status[j].node;
			dblink[j].visible = openStatus.status[j].link;

			if( dblink[j].nodeZ.outLinks.length != 0){ 
				dbOpen(dblink[j].nodeZ.outLinks, openStatus.status[j]);
			}
		}
	}
}

使用姿勢:


var thatX,thatY,thisX,thisY;
scene.addEventListener('mousedown', function(e){
	if(e.button == 0){
		thatX = e.offsetX;
		thatY = e.offsetY;
	}
});
scene.addEventListener('mouseup', function(event) {
	if(event.button == 0){
		thisX = event.offsetX;
		thisY = event.offsetY;
		var distanceX = Math.abs(thisX - thatX);
		var distanceY = Math.abs(thisY - thatY);
		if(distanceX > 20 || distanceY > 20){ 
			// 區分拖拽和單擊事件
		}else if(event.target && event.target.elementType == "node"){
			event.target.removeEventListener("click");
			event.target.addEventListener("click",function(event){
				foldOpen(event); // 摺疊與展開子節點功能
			});
		}
	}
});

需要注意,在節點創建的時候需要添加 click 事件監聽,哪怕什麼也不做:

function addNode(text){
	var node = new JTopo.Node(text);

	node.addEventListener("click",function(event){
		// 此事件不可刪除
	});

	scene.add(node);
	return node;
}

完整的使用例子:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title> jtopo 子節點摺疊展開 + 子節點環形佈局 DEMO </title>
    <style>
		body{
			padding: 0;
			margin: 0;
		}
		.controlLeftBtn{
			width: 50px;
			height: 31px;
			cursor: pointer;
			position: absolute;
			
		}
    </style>
</head>
<body>
	<div>
		<canvas  id="canvas" width="1707" height="826"></canvas>
	</div>
<script type="text/javascript" src="js/jtopo-min.js"></script>
<script type="text/javascript" src="js/jquery.js"></script>
<script>

	// 高度響應式
	let canvasDom = document.querySelector("#canvas");
	canvasDom.setAttribute('height',document.documentElement.clientHeight - 5);
	canvasDom.setAttribute('width',document.documentElement.clientWidth - 3);


	// 拓撲圖數據
	var myData = [
				{"hostName":"SWITCH_192_168_123_43","hostType":"2","hostHModel":"MS3228","hostStatus":"1","list":[
					{"hostName":"SWITCH_192_168_123_63","hostType":"2","hostHModel":"MS3228","hostStatus":"1","list":[
						{"hostName":"SWITCH_192_168_123_64","hostType":"2","hostHModel":"MS3228","hostStatus":"1","list":[
							{"hostName":"AP192-168-123-62","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-61","hostType":"1","hostStatus":"1","hostHModel":"MP223"}
						]}
					]},
					{"hostName":"SWITCH_192_168_123_63","hostType":"2","hostHModel":"MS3228","hostStatus":"1","list":[
						{"hostName":"SWITCH_192_168_123_64","hostType":"2","hostHModel":"MS3228","hostStatus":"1","list":[
							{"hostName":"AP192-168-123-62","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-61","hostType":"1","hostStatus":"1","hostHModel":"MP223"}
						]},
						{"hostName":"SWITCH_192_168_123_64","hostType":"2","hostHModel":"MS3228","hostStatus":"1","list":[
							{"hostName":"AP192-168-123-62","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-61","hostType":"1","hostStatus":"1","hostHModel":"MP223","list":[
								{"hostName":"AP192-168-123-62","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-61","hostType":"1","hostStatus":"1","hostHModel":"MP223"}
							]}
						]}
					]},
					{"hostName":"SWITCH_192_168_123_63","hostType":"2","hostHModel":"MS3228","hostStatus":"1","list":[
						{"hostName":"SWITCH_192_168_123_64","hostType":"2","hostHModel":"MS3228","hostStatus":"1","list":[
							{"hostName":"AP192-168-123-62","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
							{"hostName":"AP192-168-123-61","hostType":"1","hostStatus":"1","hostHModel":"MP223","list":[
								{"hostName":"AP192-168-123-62","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
								{"hostName":"AP192-168-123-61","hostType":"1","hostStatus":"1","hostHModel":"MP223","list":[
									{"hostName":"AP192-168-123-62","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
									{"hostName":"AP192-168-123-61","hostType":"1","hostStatus":"1","hostHModel":"MP223","list":[
										{"hostName":"AP192-168-123-62","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-47","hostType":"1","hostStatus":"1","hostHModel":"MP223"},
										{"hostName":"AP192-168-123-61","hostType":"1","hostStatus":"1","hostHModel":"MP223"}
									]}
								]}
							]}
						]}
					]}
				]},
				{"hostName":"AP192-168-123-32","hostType":"1","hostHModel":"MP223","hostStatus":"1"},
				{"hostName":"AP192-168-123-32","hostType":"1","hostHModel":"MP223","hostStatus":"1"},
				{"hostName":"AP192-168-123-32","hostType":"1","hostHModel":"MP223","hostStatus":"1"},
				{"hostName":"AP192-168-123-32","hostType":"1","hostHModel":"MP223","hostStatus":"1"},
				{"hostName":"AP192-168-123-32","hostType":"1","hostHModel":"MP223","hostStatus":"1"},
				{"hostName":"AP192-168-123-32","hostType":"1","hostHModel":"MP223","hostStatus":"1"},
				{"hostName":"AP192-168-123-51","hostType":"1","hostHModel":"MP223","hostStatus":"1"},
				{"hostName":"AP192-168-123-37","hostType":"1","hostHModel":"MP223","hostStatus":"1"}
			];
	
	// 畫圖
	drawTopo(myData);
	function drawTopo(data){
		var _strokeColor = '129,169,129'; 		// 線條顏色
		var _strokeColorFalut = '210,129,129';	// 離線線條顏色

		// 分離不同類型的節點
		var swiArrHasList = []; // 存在下層節點的交換機
		var justArr = [];    	// 不存在下層節點的交換機或者 AP
		for(var i=0;i<data.length;i++){
			if(data[i].hostType == '2' && data[i].list ){ // 交換機,有下層節點
				swiArrHasList.push(data[i]);
			}
			if((data[i].hostType == '2' && !data[i].list) || (data[i].hostType == '1') ){ // 交換機,沒有下層節點 或者 ap
				justArr.push(data[i])
			}
		}

		// 開始畫圖
		var canvas = document.getElementById('canvas');
		var stage = new JTopo.Stage(canvas);
		var scene = new JTopo.Scene();
		var thatX,thatY,thisX,thisY;
		scene.addEventListener('mousedown', function(e){
			if(e.button == 0){
				thatX = e.offsetX;
				thatY = e.offsetY;
			}

		});
		scene.addEventListener('mouseup', function(event) {
			if(event.button == 0){
				thisX = event.offsetX;
				thisY = event.offsetY;
				var distanceX = Math.abs(thisX - thatX);
				var distanceY = Math.abs(thisY - thatY);
				if(distanceX > 20 || distanceY > 20){ // 區分拖拽和單擊事件
					if(event.target && event.target.elementType == "node"){
						event.target.removeEventListener("click");
						if (event.target && event.target.layout) {
							JTopo.layout.layoutNode(scene, event.target, true);
						}
					}
				}else if(event.target && event.target.elementType == "node"){
					event.target.removeEventListener("click");
					event.target.addEventListener("click",function(event){
					
						foldOpen(event);
					});
				}
			}
		});
		
		// 流式佈局(水平、垂直間隔)
		var flowLayout = JTopo.layout.FlowLayout(100, 40); 

		stage.wheelZoom = 1.25; // 滾輪縮放
		scene.alpha = 1; 		// 通道透明度
		scene.backgroundColor = '54,57,63';
		stage.add(scene);

		function addNode(text){
			var node = new JTopo.Node(text);
			scene.add(node);
			node.addEventListener("click",function(event){
				// 此事件不可刪除
			});

			// 修改節點選中樣式
			node.paintSelected = function (a){	
				 0 != this.showSelected && (a.save(), a.beginPath(), a.strokeStyle = "rgba(218,221,221, 0.7)", a.fillStyle = "rgba(218,221,221,0.7)", a.arc( 0, 0, this.width/2 + 3, 0, Math.PI*2, false), a.fill(), a.stroke(), a.closePath(), a.restore())
			};

			return node;
		}

		function addLink(nodeA, nodeZ, linkColor){
			var link = new JTopo.Link(nodeA, nodeZ);
			link.strokeColor = linkColor ||'204,204,204';
			link.lineWidth = 1;
			scene.add(link);

			return link;
		}
		
		// 不存在下層節點的設備,通過容器佈局
		if(justArr.length != 0 ){
			var containerJustArr = new JTopo.Container('Just Container');
			containerJustArr.textPosition = 'Top_Center';
			containerJustArr.dragable = false;
			containerJustArr.fontColor = '180,180,180';
			containerJustArr.font = '14pt 微軟雅黑';
			containerJustArr.alpha = 0; // 容器顯示
			containerJustArr.fillColor = '242,242,242';
			containerJustArr.borderColor = '50,50,50';
			containerJustArr.borderRadius = 10; // 圓角
			containerJustArr.layout = flowLayout; // 不指定佈局的時候,容器的佈局爲自動(容器邊界隨元素變化)
			scene.add(containerJustArr);

			var _justLen = justArr.length;
			var justcontainerW = Math.round(Math.sqrt(_justLen))*(110+20)/2; // 根據節點格式計算盒子寬度
			var justcontainerH = Math.round(Math.sqrt(_justLen))*(100+20);

			// 如果只有不存在下層節點的設備
			if(swiArrHasList.length == 0 ){ 
				justcontainerW = Math.round(Math.sqrt(_justLen))*(110+20)*1.5;
				justcontainerH = Math.round(Math.sqrt(_justLen))*(100+20)/1.5;
			}
			containerJustArr.setBound(100, 100, justcontainerW, justcontainerH);

			for(var _ja = 0; _ja<justArr.length; _ja++){
				var justNode = addNode(justArr[_ja].hostName);
				scene.add(justNode);
				containerJustArr.add(justNode)
			}
		}


		// 存在下層節點的設備使用環形佈局
		if(swiArrHasList.length != 0){

			var _hasListLen = swiArrHasList.length;
			var centerRadius = 500;
			var thisHostLayer = getHostLayerNum(_hasListLen); // 獲取每個中心交換機的層數
			
			function getHostLayerNum(_hasListLen){
				var next = 0;
				var hostArr = [];
				for(var _w=0; _w<_hasListLen; _w++){
					hostArr[_w] = 0;
					getLayerNum(swiArrHasList[_w],1,_w)
				}
				// 獲取每個中心交換機的層數
				function getLayerNum(node,num,_w){ //  節點,層數,第幾個中心節點
					if(node.list){
						num++;
						for(var _l=0; _l<node.list.length; _l++){
							getLayerNum(node.list[_l],num,_w);
						}
					}else{
						next = num;

						if(num > hostArr[_w]){
							hostArr[_w] = num;
						}

						next = 0;
					}
				}
				return hostArr;
			}

			var positionArr = [];
			var justLen = justArr.length || 1;
			var listNodeTop = 50 * justLen;

			for(var i = 0; i < _hasListLen; i++){  // 畫中心交換機

				var listNode = addNode(swiArrHasList[i].hostName);
				var nextLevelLen = swiArrHasList[i].list ? swiArrHasList[i].list.length : 0;
				var centerRadius = nextLevelLen ? (nextLevelLen * 300)/(2 * Math.PI) : 130;
				var listRadius = 0;
				if(swiArrHasList[i].list ){ // 畫下層設備
					console.log("================ level "+ (thisHostLayer[i]-1) +"=================");
					listRadius = dragListDevice(thisHostLayer[i]-1, swiArrHasList[i].list, listNode);
				}

				// 中心交換機的位置
				listNode.setLocation(listRadius * (thisHostLayer[i]-1), listNodeTop);
				listNode.layout = {type: 'circle', radius: centerRadius + listRadius};

				scene.add(listNode);
				
				JTopo.layout.layoutNode(scene, listNode, true);  // 圓形佈局
			}

			// 畫下層設備
			function dragListDevice(layerNum, list, listNode){
				var circleRadius = 130;
				for(var i = 0; i < list.length; i++){
					var nextLevelLen = list[i].list ? list[i].list.length : 0;
					circleRadius = nextLevelLen ? (nextLevelLen * 150)/(2 * Math.PI) : 130; 

					var dlNode = addNode(list[i].hostName);

					// 如果存在下層設備
					if( list[i].list && layerNum-1){
						circleRadius += dragListDevice(layerNum-1, list[i].list, dlNode);
					}

					dlNode.layout = {type: 'circle', radius: circleRadius };

					var dlLink = addLink(listNode, dlNode);
					dlLink.strokeColor = list[i].hostStatus == 1 ? _strokeColorFalut :  _strokeColor;

					scene.add(dlNode);
					scene.add(dlLink);
				}
				return circleRadius
			}

		}
		stage.centerAndZoom(); //縮放並居中顯示
//		stage.zoomIn();

	}


	function rd(n,m){ // JS獲取n至m隨機整數
		var c = m-n+1;
		return Math.floor(Math.random() * c + n);
	}


	var foldOpenStatus = {}; // 記錄摺疊狀態
	function foldOpen(e){ // 摺疊展開
		var thisNode = e.target.text; // 以當前節點名稱爲 key
		var tarlink = e.target.outLinks;

		if(tarlink == undefined){
			return
		}
	
		if(tarlink.length != 0 && tarlink[0].visible === true){
			var status = [];

			for (var i = 0; i < tarlink.length; i++){
				status[i] = {node: tarlink[i].nodeZ.visible, link: tarlink[i].visible};
				foldOpenStatus[thisNode] = status;
				tarlink[i].nodeZ.visible = false;
				tarlink[i].visible = false;
				
				// 下一層還有節點
				if( tarlink[i].nodeZ.outLinks.length != 0){ 
					dbfold(tarlink[i].nodeZ.outLinks, foldOpenStatus[thisNode][i]);
				}
			}
		}else if(tarlink.length != 0 && tarlink[0].visible === false){
			for (var k = 0; k < tarlink.length; k++){
				tarlink[k].nodeZ.visible =  foldOpenStatus[thisNode][k].node;
				tarlink[k].visible = foldOpenStatus[thisNode][k].link;

				 // 下一層還有節點
				if( tarlink[k].nodeZ.outLinks.length != 0){
					dbOpen(tarlink[k].nodeZ.outLinks, foldOpenStatus[thisNode][k]);
				}
			}
		}

		function dbfold(dblink,foldStatus){
			var status = [];
			for(var j = 0; j < dblink.length; j++){
				status[j] = {node: dblink[j].nodeZ.visible, link: dblink[j].visible};
				foldStatus.status = status;
				dblink[j].nodeZ.visible = false;
				dblink[j].visible = false;

				if( dblink[j].nodeZ.outLinks.length != 0){ 
					dbfold(dblink[j].nodeZ.outLinks, foldStatus.status[j]);
				}
			}
		}
		function dbOpen(dblink, openStatus){
			for(var j = 0; j < dblink.length; j++){
				dblink[j].nodeZ.visible = openStatus.status[j].node;
				dblink[j].visible = openStatus.status[j].link;

				if( dblink[j].nodeZ.outLinks.length != 0){ 
					dbOpen(dblink[j].nodeZ.outLinks, openStatus.status[j]);
				}
			}
		}
	}

</script>
</body>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章