前端開發框架總結之利用Jtopo實現網路拓撲功能(一)
前端開發框架總結之利用Jtopo實現網路拓撲功能(一)
前言:
前段時間由於專案需要實現一個網路裝置拓撲管理的功能,經過一番比較,最終選擇了利用JTopo外掛來實現相關功能。首先JTopo是免費的,其次官網上的一些拓撲示例還算比較全面,能覆蓋大部分拓撲場景,再次個人認為JTopo從功能定位和API設計及實現來講還是很清晰且簡潔的,比較容易理解和上手使用。基於這三點原因加入了JTopo的二次開發大軍。
在Jtopo的二次開發過程中,有一些使用的心得再次分享出來,供大家參考。
- 拓撲工具欄
為了實現一個易用的拓撲管理功能,工具欄是必不可少的。在官網的示例中,使用了一個Toolbar的外掛,檢視其實現無非是簡單佈局+JTopo基礎api的呼叫,只是樣式實在太醜。因此在實際專案中,如果你想實現一個漂亮的風格一致的拓撲工具欄,那還是完全自己實現把。
- 拓撲結點的生成
使用js建立一個拓撲結點這個官方demo中已經很詳細了,我們使用API可以設定結點的background、size、text、image、location、以及字型樣式等基本屬性,另外還可以給結點自定義屬性。
但是在實際的使用場景中,初始是沒有拓撲結點的,我們需要通過一定的使用者互動操作來生成所需要的拓撲結點。這裡互動方式有很多種,比如直接拖拽頁面元素到畫布以生成相應結點,再比如可以拓撲使用者填寫結點資訊後直接生成拓撲結點等等。在我的專案中為了相容各種初始場景和操作方便,採用了通用元件庫拖拽和樹型結構的裝置樹拖拽以生成拓撲結點的方式。
元件庫的拖拽功能主要的使用的技術要點包括HTML5的draggables屬性及其相應事件監聽和JTopo結點相關API。這其中主要有drop、dragover、dragstart事件的監聽。另外此功能的實現有幾個細節:
1、可以利用div的"data-*"自定義屬性來標識被拖拽的元素的特殊屬性。
2、可以利用event.dataTransfer.setData('text',e.target.dataset.type)方法給拖拽事件附件一些想要傳遞的資料,以此我們可以根據這些值決定最終生成的拓撲的image、text等屬性。注:此處的key值需要用text,否則IE會有相容性bug。
3、必須要監聽dragover屬性。且使用preventDefault方法組織瀏覽器預設行為,否則無法監聽到drop事件。
4、拓撲元素的生成,需要注意新生成的元素的location的設定。我們可以使用drop事件的event的一些屬性,結合scene的translate、scale等值來計算出想要的位置。這裡要注意畫布縮放的場景可以會帶來的位置不準確。每個人的頁面佈局方式不同可以計算方式也不一樣,但是使用的屬性和計算邏輯是大差不差的。
元件庫佈局程式碼片段:
<div class="etree_scroll my_scroll" style="padding: 0 10px 10px;height: 100%;overflow-y: auto;box-sizing: border-box">
<div id="{{x.id}}" ng-repeat="x in shortcuts" data-type="{{x.type}}" style="width: 50px;display: inline-block;margin:2px 2px;" draggable="true" on-finish-render="onShortcutLoaded">
<img src={{x.img}} style="display: block;margin: 0 auto" draggable="false">
<span style="width: 50px;text-align: center;display: inline-block">{{x.name}}</span>
</div>
</div>
js程式碼片段:
function onShortcurDrag(e) {
e.dataTransfer.setData("text",e.target.dataset.type);
}
function onShortcutDrop(event) {
event.preventDefault();
var type = event.dataTransfer.getData('text');
if(type){
var shortcut;
$scope.shortcuts.forEach(function (item) {
if(item.type == type){
shortcut = item;
}
});
if(shortcut){
//var node = new JTopo.Node(shortcut.name);
var node = new JTopo.Node('點選右鍵關聯裝置');
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.screenX);
console.log("stage.scaleX:" + stage.scaleX);
var coordinate = calcLocation(event.offsetX - 25 - scene.translateX, event.offsetY - 25 - scene.translateY);
node.setLocation(coordinate.x, coordinate.y);
node.setImage(shortcut.topo);
node.setSize(50,50);
node.fontColor = '100,105,116';
node.type = shortcut.type;
node.virtualId = new Date().getTime();
node.addEventListener('mousedrag',nodeMouseDrag);
node.addEventListener('mouseup',nodeMouseUp);
node.addEventListener('dbclick',nodeDbClick);
node.addEventListener('click',nodeClick);
scene.add(node);
currentDragNode = node;
}
}
}
function onShortcutDragOver(e) {
//此處不可少,否則會監聽不到drop事件。
e.preventDefault();
}
$scope.onShortcutLoaded = function () {
//繫結dragstart事件。
$scope.shortcuts.forEach(function (shortcut) {
var elem = document.getElementById(shortcut.id);
elem.addEventListener('dragstart',onShortcurDrag);
});
//繫結drop事件。
var elem = document.getElementById("canvas");
elem.addEventListener('drop',onShortcutDrop);
elem.addEventListener('dragover',onShortcutDragOver);
}
另一種是利用裝置樹的拖拽直接生成拓撲結點。裝置樹的實現使用了zTree外掛。這種方式跟元件庫的思路相似只不過是利用的ztree的一些時間監聽而已。
js程式碼片段
function dropTree2Dom(event, treeId, treeNodes, targetNode, moveType) {
if(event.target && event.target.id == 'canvas'){
console.log("dropTree2Dom:" + treeNodes);
var node = new JTopo.Node(treeNodes[0].name);
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.screenX);
console.log("stage.scaleX:" + stage.scaleX);
//node.setLocation(event.offsetX - 25 - scene.translateX, event.offsetY - 25 - scene.translateY);
var coordinate = calcLocation(event.offsetX - 25 - scene.translateX, event.offsetY - 25 - scene.translateY);
node.setLocation(coordinate.x, coordinate.y);
node.setImage(getImageByType(treeNodes[0].type));
node.setSize(50,50);
node.fontColor = '100,105,116';
node.addEventListener('mousedrag',nodeMouseDrag);
node.addEventListener('mouseup',nodeMouseUp);
node.addEventListener('dbclick',nodeDbClick);
node.addEventListener('click',nodeClick);
node.deviceId = treeNodes[0].deviceId;
node.type = treeNodes[0].type;
node.virtualId = new Date().getTime();
scene.add(node);
currentDragNode = node;
}
}
我們新生成的拓撲結點增加了兩個自定義的屬性deviceId、virtualId,分別用來標識結點關聯的真實裝置id和結點自身的唯一標識。這些自定義屬性的使用可以幫助我們更好的實現一些真實的拓撲場景。