前端開發框架總結之利用Jtopo實現網路拓撲功能(二)
前端開發框架總結之利用Jtopo實現網路拓撲功能(二)
上文我們講了一些拓撲結點生成的實際場景設計和實現思路以及一些關鍵技術細節。本文我們繼續我們的拓撲管理開發之旅。
-
拓撲連線
給拓撲結點新增連線是拓撲管理中必不可少的操作,官網的例子中也有相應的例子。但是如果我們完全按照這個例子的操作方式用到我們專案中的話,其實是很不可取的,例子中是單擊結點進行連線,但是實際操作過程中很多時候單擊只是為了選中結點而已,然後進行結點的拖拽,拉伸等操作。另外單擊操作也會和雙擊等操作混在一起,導致連線操作互動體驗很差。本人在這裡推薦一種互動方式供大家參考:我們可以把雙擊結點的操作定義為連線的觸發動作,把雙擊後對結點的單擊動作作為連線的結束動作,經實際操作體驗是很不錯的。
官方提供的API中只有生成拓撲連線的方法。我們想要實現在連線過程中線的終點跟隨滑鼠移動效果,其實就是不斷的設定連線的一個Node的position。這個場景的實現過程中有三個關鍵點,1、記錄連線起點變數的控制。2、虛擬連線的控制,包括終點位置的設定和虛擬連線的刪除等。(為了視覺體驗優化,可以設定此虛擬連線為虛線)。3、scene的mousemove事件的監聽。
js程式碼片段
function nodeDbClick(e){ console.log("nodeDbClick"); beginNode = e.target; tempNodeS.setLocation(beginNode.x + beginNode.width*0.5, beginNode.y + beginNode.height*0.5); tempNodeE.setLocation(e.x, e.y); scene.add(tempLink); } function nodeClick(e){ console.log("nodeClick"); if(beginNode){ if(beginNode != e.target){ var link = new JTopo.Link(beginNode, e.target); link.strokeColor = '181,190,196'; scene.add(link); link.addEventListener('mouseup',linkMouseUp); beginNode = null; scene.remove(tempLink); } else{ beginNode = null; scene.remove(tempLink); } } else{ scene.remove(tempLink); } } function sceneMouseUp(e){ console.log("sceneMouseUp"); if(e.target){ if(e.target instanceof JTopo.Node){ if(e.target instanceof JTopo.TextNode){ scene.remove(tempLink); beginNode = null; } } else{ scene.remove(tempLink); beginNode = null; } if(nodeSelected){ if(nodeSelected != e.target){ nodeSelected = undefined; var elem = document.getElementById('menu'); elem.style.visibility = 'hidden'; } } if(linkSelected){ if(linkSelected != e.target){ linkSelected = undefined; var elem = document.getElementById('linkMenu'); elem.style.visibility = 'hidden'; } } } else{ scene.remove(tempLink); beginNode = null; //右鍵畫布會隱藏選單。和畫布的click事件配合使用。 if(e.button == 2){ nodeSelected = undefined; var elem = document.getElementById('menu'); elem.style.visibility = 'hidden'; linkSelected = undefined; var elem = document.getElementById('linkMenu'); elem.style.visibility = 'hidden'; } } } function sceneMouseMove(e){ //如果連線已經確認了起點,證明已經開始劃線,則終點需要隨滑鼠移動。 if(beginNode){ tempNodeE.setLocation(e.x, e.y); } }
- 拓撲文字
拓撲文字物件可以作為拓撲管理中的一個輔助元素,用以在拓撲圖中插入一段文字。拓撲文字物件是TextNode型別。注意如果使用scene的findAPI查詢node型別的結點是不包括textNode型別的結點的。此功能我是把他作為拓撲工具欄出的一個快捷按鈕來設計的。實現此功能會設計到兩個關鍵點:1、文字結點的location設定為多少合適。2、文字節點的內容如何修改。
首先,文字節點的位置這個問題好處理,只不過我們實際操作中需要注意由於scene的拖拽和縮放等造成的textnode移位問題。tips:在拓撲操作過程中經常會遇到一些由於畫布拖拽和縮放等引起的位置不準確問題,這個我們只要瞭解了JTopo的拖拽和縮放的實現基本就能很好的解決遇到的各種位置問題。拖拽對應的是scene的translateX、translateY屬性,就是x、y軸方向上的移動。縮放對應的是scene的scaleX、scaleY屬性,只不過縮放操作是以當前視野中的區域的中心為縮放參考點,以拓撲元素和這個參考點的連線為軸線進行縮放操作的。也就是說縮放時,拓撲元素的運動軌跡永遠是在這個軸線上的。另外還需考慮拓撲結點本身的size屬性受縮放的影響。
其次,文字節點內容的修改方式,可以參考官方示例的操作邏輯把輸入介面美化就可以了。這個功能的難點同樣是文字輸入介面的位置的計算,需要加入縮放、移位等因素的考慮。另外還有一個小的細節是記得不要把textnode的text設定為空,否則文字節點的寬度為0,你就無法設計結點的刪除操作了。
js程式碼
$scope.addTopoText = function () {
var center = getCenterXY();
var textNode = new JTopo.TextNode('雙擊編輯文字');
debugger;
/*textNode.height = 50;
textNode.width = 220;
textNode.borderColor = '0,255,0';
textNode.borderWidth = 1;*/
//textNode.setBound(0,0);
textNode.fontColor = '0,0,0';
textNode.font = 'bold 14px Consolas';
textNode.textPosition = 'Middle_Center';
/*
textNode.fontColor = '255,0,0';
textNode.borderRadius = 20; // 圓角
textNode.fillColor = '255,255,255';
textNode.alpha = 1;*/
scene.add(textNode);
console.log("textNode,width:" + textNode.width + ",height:" + textNode.height);
/**由於文字節點未在介面繪製,所以目前是無法獲取節點的長和寬的,但是我們生成的結點初始內容是固定的,
* 經過測試width:96,height:12,後續字型有調整的話,需要根據實際情況修改一下。
**/
textNode.setLocation(center.x - 48,center.y - 6);
textNode.virtualId = new Date().getTime();
textNode.addEventListener('mousedrag',nodeMouseDrag);
textNode.addEventListener('dbclick',textDbClick);
textNode.addEventListener('mouseup',linkMouseUp);
}
function textDbClick(event) {
var e = event.target;
var elem = angular.element('#textNodeName');
elem = document.getElementById("textNodeName");
elem.value = e.text;
e.text = "";
elem.JTopoNode = e;
console.log("event,X:" + event.x + ",y:" + event.y);
console.log("event,tX:" + event.tx + ",ty:" + event.ty);
console.log("event,pageX:" + event.pageX + ",pageY:" + event.pageY);
console.log("event,offsetX:" + event.offsetX + ",offsetY:" + event.offsetY);
console.log("event,clientX:" + event.clientX + ",clientY:" + event.clientY);
console.log("event,screenX:" + event.screenX + ",screenY:" + event.screenY);
console.log("e.x:" + ( e.x + scene.translateX) + ",e.y:" + ( e.y + scene.translateY) );
//$scope.textNodeStyle.top = event.offsetY + yOffset - 0;
//$scope.textNodeStyle.left = event.offsetX + xOffset - 0;
$scope.textNodeStyle.top = e.y + scene.translateY + yOffset - 5;
$scope.textNodeStyle.left = e.x + scene.translateX + xOffset - 5;
$scope.textNodeStyle.visibility = "visible";
$scope.$apply();
$timeout(function () {
elem.focus();
});
}
$scope.setTextNodeName = function (event) {
if(event.target.value.length > 0){
event.target.JTopoNode.text = event.target.value;
}
else{
event.target.JTopoNode.text = '雙擊編輯文字';
}
$scope.textNodeStyle.visibility = "hidden";
$scope.$apply();
}