基於Jtopo的網路拓撲編輯器初探

来源:http://www.cnblogs.com/gongxufan/archive/2017/11/03/7779578.html
-Advertisement-
Play Games

為了方便演示,我已經把一個靜態DEMO部署到github,傳送門 關於項目訪問,因為讀取遠程json數據,直接用瀏覽器打開有安全限制.請將項目放到tomcat啟動訪問或者直接用idea/webstorm打開項目直接右鍵打開,如下圖所示: 完整代碼單獨放到了github:https://github. ...


為了方便演示,我已經把一個靜態DEMO部署到github,傳送門

關於項目訪問,因為讀取遠程json數據,直接用瀏覽器打開有安全限制.請將項目放到tomcat啟動訪問或者直接用idea/webstorm打開項目直接右鍵打開,如下圖所示:


完整代碼單獨放到了github:https://github.com/gongxufan/web-topology

基本的操作可以看下麵的GIF圖,節點從左邊圖標欄拖拽至編輯區,通過單擊節點圖標,然後鬆開滑鼠按鍵則可拖動一條連線。在目標節點單擊,怎完成一次連線操作。左欄可以選擇多種連線方式。



這個拓撲編輯器UI是easyUI做的佈局,節點的編輯和連線則是基於jTopo二次開發的。jTopo提供了Stage、Scene、Node、Container以及對動畫的支持,API使用相對簡單易用。缺點是文檔缺乏,但是我們可以通過作者提供的DEMO進行熟悉。一旦熟悉其源碼,則十分便於二次開發。用來實現一些動畫也是輕而易舉的。由於是基於canvas的繪畫,所以每次更改和操作其實都會重繪整個畫布。當中還是有不少可以優化的地方。其實想D3.js也可以走到這樣的效果,只是近期工作都在後端就沒去探究了。

由於只是摘取個人開發項目的前端部分,由於公司許可原因只能開發這一部分了,有興趣的可以去我的github上clone下來參考。核心部分代碼都在editor.js,提供了節點拖拽、節點連線、佈局等方法支持。這個是一個初始版本。代碼粗糙,僅供參考。

下麵摘取幾個重要的地方稍微講解:

1、編輯器初始化代碼

 //創建JTOP舞臺屏幕對象
    var canvas = document.getElementById('drawCanvas');
    canvas.width = $("#contextBody").width();
    canvas.height = $("#contextBody").height();
    //載入空白的編輯器
    if(stageJson == "-1"){
        this.stage = new JTopo.Stage(canvas);
        this.stage.topoLevel = 1;
        this.stage.parentLevel = 0;
        this.modeIdIndex = 1;
        this.scene=  new JTopo.Scene(this.stage);
        this.scene.totalLevel = 1;
    }else{
        this.stage = JTopo.createStageFromJson(stageJson, canvas);
        this.scene = this.stage.childs[0];
    }

 

我們可以調用JTopo.Stage(canvas)來初始化一個畫圖區域,接下來就可以使用API進行節點和動畫的操作了。整個畫圖對象的JSON層次結構如下所示:

{
  "version": "0.4.8",
  "deviceNum": "19",
  "wheelZoom": 0.95,
  "width": 864,
  "height": 569,
  "id": "ST172.19.105.52015100809430700001",
  "topoLevel": "1",
  "parentLevel": "0",
  "nextLevel": "0",
  "childs": [
    {
      "elementType": "scene",
      "id": "S172.19.105.52015100809430700002",
      "topoLevel": "1",
      "parentLevel": "0",
      "nextLevel": "0",
      "translateX": 106.5,
      "translateY": 20,
      "scaleX": 1,
      "scaleY": 1,
      "totalLevel": "1",
      "childs": [
        {
          "elementType": "node",
          "id": "",
          "topoLevel": 1,
          "parentLevel": "0",
          "nextLevel": "0",
          "x": 211.5,
          "y": 135,
          "width": 32,
          "height": 32,
          "visible": true,
          "rotate": 0,
          "scaleX": 1,
          "scaleY": 1,
          "zIndex": 3,
          "deviceId": "1404683827351.4666",
          "dataType": "VR",
          "nodeImage": "tpIcon_9.png",
          "text": "CS路由器",
          "textPosition": "Bottom_Center",
          "templateId": undefined
        }
      ]
    }
  ]
}

其結構為:

 


通常我們只需要一個 Scene對象即可管理所有的對象,當然如果要實現更複雜的分組對象管理則可以創建多個Scene對象進行單獨管理。同時我們可以調用JTopo.createStageFromJson(stageJson, canvas)方法來講一個保存好的拓撲結構重新渲染。

2、節點的拖拽

節點的拖拽使用的原生H5的drag&drop來實現

/**
 * 圖元拖放功能實現
 * @param modeDiv
 * @param drawArea
 */
networkTopologyEditor.prototype.drag = function (modeDiv, drawArea, text) {
    if (!text) text = "";
    var self = this;
    //拖拽開始,攜帶必要的參數
    modeDiv.ondragstart = function (e) {
        e = e || window.event;
        var dragSrc = this;
        var backImg = $(dragSrc).find("img").eq(0).attr("src");
        backImg = backImg.substring(backImg.lastIndexOf('/') + 1);
        var datatype = $(this).attr("datatype");
        try {
            //IE只允許KEY為text和URL
            e.dataTransfer.setData('text', backImg + ";" + text + ";" + datatype);
        } catch (ex) {
            console.log(ex);
        }
    };
    //阻止預設事件
    drawArea.ondragover = function (e) {
        e.preventDefault();
        return false;
    };
    //創建節點
    drawArea.ondrop = function (e) {
        e = e || window.event;
        var data = e.dataTransfer.getData("text");
        var img, text,datatype;
        if (data) {
            var datas = data.split(";");
            if (datas && datas.length == 3) {
                img = datas[0];
                text = datas[1];
                datatype = datas[2];
                var node = new JTopo.Node();
                node.fontColor = self.config.nodeFontColor;
                node.setBound((e.layerX ? e.layerX : e.offsetX) - self.scene.translateX - self.config.defaultWidth / 2, (e.layerY ? e.layerY : e.offsetY) - self.scene.translateY - self.config.defaultHeight / 2,self.config.defaultWidth,self.config.defaultHeight);
                //設備圖片
                node.setImage(context + 'post/web-topology/icon/' + img);
                //var cuurId = "device" + (++self.modeIdIndex);
                var cuurId = "" + new Date().getTime() * Math.random();
                node.deviceId = cuurId;
                node.dataType = datatype;
                node.nodeImage = img;
                ++self.modeIdIndex;
                node.text = text;
                node.layout = self.layout;
                //節點所屬層次
                node.topoLevel = parseInt($("#selectLevel").find("option:selected").val());
                //節點所屬父層次
                node.parentLevel = $("#parentLevel").val();
                //子網連接點的下一個層,預設為0
                node.nextLevel = "0";
                self.scene.add(node);

                //載入屬性面板
                /* if(self.currDataType)
                 self.clearOldPanels(self.currDataType)
                 self.currDeviceId = cuurId;
                 self.createNewPanels(datatype,self.templateId,self.currentModeId);*/
                //self.currDataType = datatype;
                self.currentNode = node;
            }
        }
        if (e.preventDefault()) {
            e.preventDefault();
        }
        if (e.stopPropagation()) {
            e.stopPropagation();
        }
    }
}

在ondragstart回調方法中傳遞底圖以及必要參數,然後在ondrop進行節點的創建

 

新建節點使用JTopo.Node()構造,設置好相關屬性然後通過scene.add(node)加入到Stage。為何執行add操作在界面上就可以看到新的節點了呢?原因是Stage有一個frames屬性,它定義了畫布重繪頻率1000/frames。

frames [屬性]

設置當前舞臺播放的幀數/秒

預設為:24

frames可以為0,表示:不自動繪製,由用戶手工調用Stage對象的paint()方法來觸發。

如果小於0意味著:只有鍵盤、滑鼠有動作時才會重繪,例如:stage.frames = -24。

 

預設畫面幀數為24幀,也就是每1000/24ms就會重繪屏幕。後臺刷新的代碼如下:

 

function() {
                    0 == stage.frames ? setTimeout(arguments.callee, 100) : stage.frames < 0 ? (stage.repaint(),
                        setTimeout(arguments.callee, 1e3 / -stage.frames)) : (stage.repaint(),
                        setTimeout(arguments.callee, 1e3 / stage.frames))
                }
                ()
setTimeout會調用下麵的重繪函數,

 

              this.paint = function() {
                    null  != this.canvas && (this.graphics.save(),
                        this.graphics.clearRect(0, 0, this.width, this.height),
                        this.childs.forEach(function(a) {
                                1 == a.visible && a.repaint(stage.graphics)
                            }
                        ),
                    1 == this.eagleEye.visible && this.eagleEye.paint(this),
                        this.graphics.restore())
                }
                ,
                this.repaint = function() {
                    0 != this.frames && (this.frames < 0 && 0 == this.needRepaint || (this.paint(),
                    this.frames < 0 && (this.needRepaint = !1)))
                }
paint對遍歷所有可見對象 ,依次調用repaint方法。

3、節點連線

這裡採用的連線方法是在節點按下滑鼠左鍵,然後鬆開滑鼠則創建一個連線,起點是被點擊的節點,終點則隨滑鼠移動而動態更新。因此單機一個節點鬆開滑鼠則可以看到隨滑鼠移動的一條連線。然後在某個節點點擊左鍵鬆開怎完成了兩個節點的連線。效果如下:
部分代碼實現如下:

jTopo支持支線、折線、曲線等的常見,但是折線的拐角處長度現在只能在創建的時候指定。如需動態的秒點創建需要二次開發。代碼如下:
                  
if(self.lineType == "line"){
                        self.link = new JTopo.Link(self.tempNodeA, self.tempNodeZ);
                        self.link.lineType = "line";
                    }else if(self.lineType == "foldLine"){
                        self.link = new JTopo.FoldLink(self.tempNodeA, self.tempNodeZ);
                        self.link.lineType = "foldLine";
                        self.link.direction =  self.config.direction;
                    }else if(self.lineType == "flexLine"){
                        self.link = new JTopo.FlexionalLink(self.tempNodeA, self.tempNodeZ);
                        self.link.direction =  self.config.direction;
                        self.link.lineType = "flexLine";
                    }else if(self.lineType == "curveLine"){
                        self.link = new JTopo.CurveLink(self.tempNodeA, self.tempNodeZ);
                        self.link.lineType = "curveLine";
                    }
其餘細節在此不做贅述,這個項目需要讀者具備:H5、easyUI、jTopo、canvas、JSON等基本知識。至於jTopo只需看看其作者提供的DEMO和很少的API就可以很快上手。最好的學習方法是打斷點不同的調試跟蹤,查看整個工作機制。這裡演示的拓撲編輯也是一個很簡單不完整的例子,其實還是有很多可以定製化的東西,比如連線以及連線方式都可以進一步定製。 參考資料: http://www.jtopo.com/
http://www.jeasyui.com/documentation/
http://www.w3school.com.cn/html5/


 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 全局安裝webpack cnpm install --save-dev webpack 2. 初始化 cnpm init cnpm install --save-dev webpack 創建項目目錄( dist為生成目錄 ) 3. 配置文件 webpack.config.js 創建main.j ...
  • 今天在使用滑鼠事件時,用錯了mouseout,於是做個測試總結。 結論: mouseenter:當滑鼠移入某元素時觸發。 mouseleave:當滑鼠移出某元素時觸發。 mouseover:當滑鼠移入某元素時觸發,移入和移出其子元素時也會觸發。 mouseout:當滑鼠移出某元素時觸發,移入和移出其 ...
  • 面試中經常考到面向對象的一些知識,在這記錄一下,如有不對歡迎指正,願在前端的道路上共勉! 一、原型 1.什麼是原型: 簡單說就像css的class一樣,是公用的,給DOM元素加個class名就可以公用樣式,那麼原型就相當於css裡面的class,都可以用。 在構造函數創建出來的時候,系統會預設的幫構 ...
  • (web前端學習交流群:328058344 禁止閑聊,非喜勿進!) 顏色 桃紅~紛紅 顏色 紫 顏色 褐~橘~米白 顏色 金~黃 顏色 ~黃 綠 顏色 藍 顏色 黑~灰~白 顏色 ...
  • 轉自st.gg 為什麼用 $scope.user = $scope.master; $scope.master 會跟著 $scope.user 改變?angular.copy 和 = 號賦值有什麼區別呢?新手還沒有搞懂,請教各位了。 你可以這麼來理解: 記憶體里有一段地址儲存了 { firstName ...
  • 前段時間不是很忙,剛好公司需要開發一個微信小程式,於是我就入坑了(此坑還是有點深滴,請備好乾糧)。 我是一名iOS開發工程師,個人覺得入門開發小程式的話,需要基本的web前端知識,比如說:代碼的書寫格式,規範,標簽以及樣式的使用等,但作為一門如此新的開發語言,它也有自己的獨特之處,就像我們的swif ...
  • 轉自CSDN: 工作有問題上CSDN上轉轉. $apply()和$digest()在AngularJS中是兩個核心概念,但是有時候它們又讓人困惑。而為了瞭解AngularJS的工作方式,首先需要瞭解$apply()和$digest()是如何工作的。這篇文章旨在解釋$apply()和$digest() ...
  • Vue 2.5 發佈了:15篇前端熱文回看 2017-11-02 前端大全 (點擊上方公眾號,可快速關註) 本文精選了「前端大全」2017 年 10 月的 15 篇熱門文章。其中有職場分享、技術分享和技術資源。 註:以下文章,點擊標題即可閱讀 《Vue 2.5 發佈了》 我們很高興宣佈 Vue 2. ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...