首页 > 代码库 > 基于Web实现在线绘画拓扑图[GraphEditor]
基于Web实现在线绘画拓扑图[GraphEditor]
网络拓扑图本来已经整理有一段时间了,一次项目会议写集中边界监控系统的时候上级要求使用可以在系统中画网络拓扑图,没办法当时找不到现有的程序来参考
只能硬着头皮,顶着风险来完成[当然来边界安全的,当然要安全型高啊],一同事找到一些源码来分析,当然了有源码分析比自己想的效率要快得多
但是也很让人头痛,怎样才能实现,怎样才能嵌入到Web项目中?
这个集控那个项目已近完成有一段时间了,最近呢一些网友要借鉴我修改后的代码,和一些效果我最近整理了一份但是当时由于比较忙,没有发到博客中
去!只是写了一个简单的Demo供参考和利用,由于最近又有一些朋友也来问这个问题,为了方便与资源共享,我还是整理了这边文章,和网络拓扑的运用,当然
技术肯定还有更加优化好的控件,有的话希望共同学习!
下面是我编写的一个简单的Demo
这是简单画的一个拓扑图:
这是简单的绘画界面,Tab切换后是快捷键保存的后的模板[类似图表,也可以编辑],在这里就先不演示了
当然如果需要,请加入群直接下载分享文件[完整的Demo]
下面来详解下文件的配置,和代码分析
首先来看下web.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> 3 <display-name></display-name> 4 <servlet> 5 <description>This is the description of my J2EE component</description> 6 <display-name>This is the display name of my J2EE component</display-name> 7 <servlet-name>SaveToXmlServlet</servlet-name> 8 <servlet-class>grapheditor.SaveToXmlServlet</servlet-class> 9 </servlet>10 <servlet-mapping>11 <servlet-name>SaveToXmlServlet</servlet-name>12 <url-pattern>/SaveToXmlServlet</url-pattern>13 </servlet-mapping>14 <welcome-file-list>15 <welcome-file>graph.jsp</welcome-file>16 </welcome-file-list>17 </web-app>
配置不多,相信熟练Web的开发的这个就不用解释了,一看便能理解其中的配置,这里就不详细介绍了
接着我们来编写JSP页面,这里为了方便看和传输数据,我JS接直接放到一起了
1 <%@ page language="java" %> 2 <%@ page contentType="text/html; charset=utf-8"%> 3 <%String path =request.getContextPath();%> 4 <head> 5 <head> 6 <title>Graph Editor</title> 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 8 <script type="text/javascript" src=http://www.mamicode.com/"../../../../js/default/jquery-1.6.2.js"></script> 9 <script type="text/javascript" src=http://www.mamicode.com/"../../../../js/default/jquery-ui-1.8.16.custom.min.js"></script> 10 <link rel="stylesheet" type="text/css" href=http://www.mamicode.com/"styles/grapheditor.css"> 11 <script type="text/javascript"> 12 //全局变量 13 var MAX_REQUEST_SIZE = 10485760; 14 var MAX_WIDTH = 6000; 15 var MAX_HEIGHT = 6000; 16 17 //保存地址或导入地址 18 var EXPORT_URL = ‘/visecMc/ExportServlet‘; 19 var SAVE_URL = ‘/visecMc/SaveMapServlet‘; 20 var OPEN_URL = ‘/open‘; 21 var RESOURCES_PATH = ‘resources‘; 22 var RESOURCE_BASE = RESOURCES_PATH + ‘/grapheditor‘; 23 var STENCIL_PATH = ‘stencils‘; 24 var IMAGE_PATH = ‘images‘; 25 var STYLE_PATH = ‘styles‘; 26 var CSS_PATH = ‘styles‘; 27 var OPEN_FORM = ‘open.html‘; 28 29 //指定连接模式为触摸设备(至少有一个应该是正确的) 30 var tapAndHoldStartsConnection = true; 31 var showConnectorImg = true; 32 33 // 解析URL参数。支持参数: 34 // - lang = xy:指定用户界面的语言。 35 // - 触摸= 1:使touch-style用户界面。 36 // - 存储=当地:支持HTML5本地存储。 37 var urlParams = (function(url) 38 { 39 var result = new Object(); 40 var idx = url.lastIndexOf(‘?‘); 41 42 if (idx > 0) 43 { 44 var params = url.substring(idx + 1).split(‘&‘); 45 46 for (var i = 0; i < params.length; i++) 47 { 48 idx = params[i].indexOf(‘=‘); 49 50 if (idx > 0) 51 { 52 result[params[i].substring(0, idx)] = params[i].substring(idx + 1); 53 } 54 } 55 } 56 57 return result; 58 })(window.location.href); 59 60 // 设置用户界面语言的基本路径,通过URL参数和配置 61 // 支持的语言,以避免404年代。装运的所有核心语言 62 // 资源是禁用grapheditor所需的所有资源。 63 // 属性。注意,在这个例子中两个资源的加载 64 // 文件(特殊包,默认包)是禁用的 65 // 保存一个GET请求。这就要求所有资源存在 66 // 每个属性文件,因为只有一个文件被加载。 67 mxLoadResources = false; 68 mxBasePath = ‘../../../src‘; 69 mxLanguage = urlParams[‘lang‘]; 70 mxLanguages = [‘de‘]; 71 </script> 72 <script type="text/javascript" src=http://www.mamicode.com/"js/mxClient.js"></script> 73 <script type="text/javascript" src=http://www.mamicode.com/"js/Editor.js"></script> 74 <script type="text/javascript" src=http://www.mamicode.com/"js/Graph.js"></script> 75 <script type="text/javascript" src=http://www.mamicode.com/"js/Shapes.js"></script> 76 <script type="text/javascript" src=http://www.mamicode.com/"js/EditorUi.js"></script> 77 <script type="text/javascript" src=http://www.mamicode.com/"js/Actions.js"></script> 78 <script type="text/javascript" src=http://www.mamicode.com/"js/Menus.js"></script> 79 <script type="text/javascript" src=http://www.mamicode.com/"js/Sidebar.js"></script> 80 <script type="text/javascript" src=http://www.mamicode.com/"js/Toolbar.js"></script> 81 <script type="text/javascript" src=http://www.mamicode.com/"js/Dialogs.js"></script> 82 <script type="text/javascript" src=http://www.mamicode.com/"jscolor/jscolor.js"></script> 83 </head> 84 <body class="geEditor"> 85 <input type="hidden" id="mapTp" value=http://www.mamicode.com/"qsy"/> 86 <input type="hidden" id="path" value=http://www.mamicode.com/"<%=path %>"/> 87 <script type="text/javascript"> 88 // EditorUi更新扩展I / O操作状态 89 (function() 90 { 91 var editorUiInit = EditorUi.prototype.init; 92 93 EditorUi.prototype.init = function() 94 { 95 editorUiInit.apply(this, arguments); 96 //this.actions.get(‘export‘).setEnabled(false); 97 //需要一个后端更新动作状态 98 if (!useLocalStorage) 99 {100 mxUtils.post(OPEN_URL, ‘‘, mxUtils.bind(this, function(req)101 {102 var enabled = req.getStatus() != 404;103 this.actions.get(‘open‘).setEnabled(enabled || fileSupport);104 this.actions.get(‘import‘).setEnabled(enabled || fileSupport);105 this.actions.get(‘save‘).setEnabled(true);106 }));107 }108 };109 })();110 111 new EditorUi(new Editor());112 </script>113 </body>
上诉文件呢,主要负责拓扑图的绘画与相关操作界面的代码
由于相关的文件过多,我这里之举出比较重要的几个问文件
Actions.js主要获取坐标并进行处理的JS文件
1 function Actions(editorUi) 2 { 3 this.editorUi = editorUi; 4 this.actions = new Object(); 5 this.init(); 6 }; 7 8 /** 9 * 添加默认的行为 10 */ 11 Actions.prototype.init = function() 12 { 13 var ui = this.editorUi; 14 var editor = ui.editor; 15 var graph = editor.graph; 16 graph.cellsMovable=!0;//设置不可移动 17 graph.cellsDisconnectable=!0;//设置边不可编辑 18 graph.cellsResizable=!0;//设置不可改变大小 19 $.post($("#path").val()+"/SaveToXmlServlet",{"tp":$("#mapTp").val(),"type":"get"},function(text){ 20 if(text=="0"){ 21 alert("文件加载失败!"); 22 }else{ 23 var xml = text; 24 var doc = mxUtils.parseXml(xml); 25 var model = new mxGraphModel(); 26 var codec = new mxCodec(doc); 27 codec.decode(doc.documentElement, model); 28 var children = model.getChildren(model.getChildAt(model.getRoot(), 0)); 29 graph.setSelectionCells(editor.graph.importCells(children)); 30 } 31 }); 32 33 // 文件操作 34 this.addAction(‘new‘, function() { window.open(ui.getUrl()); }); 35 this.addAction(‘open‘, function() 36 { 37 window.openNew = true; 38 window.openKey = ‘open‘; 39 40 ui.openFile(); 41 }); 42 this.addAction(‘import‘, function() 43 { 44 window.openNew = false; 45 window.openKey = ‘import‘; 46 47 // 后关闭对话框打开 48 window.openFile = new OpenFile(mxUtils.bind(this, function() 49 { 50 ui.hideDialog(); 51 })); 52 53 window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename) 54 { 55 try 56 { 57 var doc = mxUtils.parseXml(xml); 58 var model = new mxGraphModel(); 59 var codec = new mxCodec(doc); 60 codec.decode(doc.documentElement, model); 61 62 var children = model.getChildren(model.getChildAt(model.getRoot(), 0)); 63 editor.graph.setSelectionCells(editor.graph.importCells(children)); 64 } 65 catch (e) 66 { 67 mxUtils.alert(mxResources.get(‘invalidOrMissingFile‘) + ‘: ‘ + e.message); 68 } 69 })); 70 71 // 删除openFile是否关闭对话框 72 ui.showDialog(new OpenDialog(this).container, 300, 180, true, true, function() 73 { 74 window.openFile = null; 75 }); 76 }); 77 this.addAction(‘save‘, function() { ui.save(); }, null, null, ‘Ctrl+S‘); 78 //addAction(saveAs,函数(){ ui.saveFile(真正);},空,空,“Ctrl + Shift-S”); 79 //addAction(“出口”,函数(){ ui。showDialog(新ExportDialog(ui)。容器、300、200,真的,真的);},空,空,“Ctrl + E”); 80 //(“editFile”,新的行动(mxResources.get(“编辑”),mxUtils。绑定(此功能() 81 //(“editFile”,新的行动(mxResources.get(“编辑”),mxUtils。绑定(此功能() 82 this.addAction(‘pageSetup‘, function() { ui.showDialog(new PageSetupDialog(ui).container, 300, 200, true, true); }); 83 this.addAction(‘print‘, function() { ui.showDialog(new PrintDialog(ui).container, 300, 200, true, true); }, null, ‘sprite-print‘, ‘Ctrl+P‘); 84 this.addAction(‘preview‘, function() { mxUtils.show(graph, null, 10, 10); }); 85 86 // Edit actions 87 this.addAction(‘undo‘, function() { editor.undoManager.undo(); }, null, ‘sprite-undo‘, ‘Ctrl+Z‘); 88 this.addAction(‘redo‘, function() { editor.undoManager.redo(); }, null, ‘sprite-redo‘, ‘Ctrl+Y‘); 89 this.addAction(‘cut‘, function() { mxClipboard.cut(graph); }, null, ‘sprite-cut‘, ‘Ctrl+X‘); 90 this.addAction(‘copy‘, function() { mxClipboard.copy(graph); }, null, ‘sprite-copy‘, ‘Ctrl+C‘); 91 this.addAction(‘paste‘, function() { mxClipboard.paste(graph); }, false, ‘sprite-paste‘, ‘Ctrl+V‘); 92 this.addAction(‘delete‘, function() { graph.removeCells(); }, null, null, ‘Delete‘); 93 this.addAction(‘duplicate‘, function() 94 { 95 var s = graph.gridSize; 96 graph.setSelectionCells(graph.moveCells(graph.getSelectionCells(), s, s, true)); 97 }, null, null, ‘Ctrl+D‘); 98 this.addAction(‘selectVertices‘, function() { graph.selectVertices(); }, null, null, ‘Ctrl+Shift+V‘); 99 this.addAction(‘selectEdges‘, function() { graph.selectEdges(); }, null, null, ‘Ctrl+Shift+E‘);100 this.addAction(‘selectAll‘, function() { graph.selectAll(); }, null, null, ‘Ctrl+A‘);101 102 // 导航103 this.addAction(‘home‘, function() { graph.home(); }, null, null, ‘Home‘);104 this.addAction(‘exitGroup‘, function() { graph.exitGroup(); }, null, null, ‘Page Up‘);105 this.addAction(‘enterGroup‘, function() { graph.enterGroup(); }, null, null, ‘Page Down‘);106 this.addAction(‘expand‘, function() { graph.foldCells(false); }, null, null, ‘Enter‘);107 this.addAction(‘collapse‘, function() { graph.foldCells(true); }, null, null, ‘Backspace‘);108 109 //安排操作110 this.addAction(‘toFront‘, function() { graph.orderCells(false); }, null, null, ‘Ctrl+F‘);111 this.addAction(‘toBack‘, function() { graph.orderCells(true); }, null, null, ‘Ctrl+B‘);112 this.addAction(‘group‘, function() { graph.setSelectionCell(graph.groupCells(null, 0)); }, null, null, ‘Ctrl+G‘);113 this.addAction(‘ungroup‘, function() { graph.setSelectionCells(graph.ungroupCells()); }, null, null, ‘Ctrl+U‘);114 this.addAction(‘removeFromGroup‘, function() { graph.removeCellsFromParent(); });115 this.addAction(‘editLink‘, function()116 {117 var cell = graph.getSelectionCell();118 var link = graph.getLinkForCell(cell);119 120 if (link == null)121 {122 link = ‘‘;123 }124 125 link = mxUtils.prompt(mxResources.get(‘enterValue‘), link);126 127 if (link != null)128 {129 graph.setLinkForCell(cell, link);130 }131 });132 this.addAction(‘openLink‘, function()133 {134 var cell = graph.getSelectionCell();135 var link = graph.getLinkForCell(cell);136 137 if (link != null)138 {139 window.open(link);140 }141 });142 this.addAction(‘autosize‘, function()143 {144 var cells = graph.getSelectionCells();145 146 if (cells != null)147 {148 graph.getModel().beginUpdate();149 try150 {151 for (var i = 0; i < cells.length; i++)152 {153 var cell = cells[i];154 155 if (graph.getModel().getChildCount(cell))156 {157 graph.updateGroupBounds([cell], 20);158 }159 else160 {161 graph.updateCellSize(cell);162 }163 }164 }165 finally166 {167 graph.getModel().endUpdate();168 }169 }170 });171 this.addAction(‘rotation‘, function()172 {173 var value = http://www.mamicode.com/‘0‘;174 var state = graph.getView().getState(graph.getSelectionCell());175 176 if (state != null)177 {178 value = http://www.mamicode.com/state.style[mxConstants.STYLE_ROTATION] || value;179 }180 181 value = http://www.mamicode.com/mxUtils.prompt(mxResources.get(‘enterValue‘) + ‘ (‘ +182 mxResources.get(‘rotation‘) + ‘ 0-360)‘, value);183 184 if (value != null)185 {186 graph.setCellStyles(mxConstants.STYLE_ROTATION, value);187 }188 });189 this.addAction(‘rotate‘, function()190 {191 var cells = graph.getSelectionCells();192 193 if (cells != null)194 {195 graph.getModel().beginUpdate();196 try197 {198 for (var i = 0; i < cells.length; i++)199 {200 var cell = cells[i];201 202 if (graph.getModel().isVertex(cell) && graph.getModel().getChildCount(cell) == 0)203 {204 var geo = graph.getCellGeometry(cell);205 206 if (geo != null)207 {208 // 旋转几何图形的大小和位置209 geo = geo.clone();210 geo.x += geo.width / 2 - geo.height / 2;211 geo.y += geo.height / 2 - geo.width / 2;212 var tmp = geo.width;213 geo.width = geo.height;214 geo.height = tmp;215 graph.getModel().setGeometry(cell, geo);216 217 //读取当前的方向并提出90度218 var state = graph.view.getState(cell);219 220 if (state != null)221 {222 var dir = state.style[mxConstants.STYLE_DIRECTION] || ‘east‘/*default*/;223 224 if (dir == ‘east‘)225 {226 dir = ‘south‘;227 }228 else if (dir == ‘south‘)229 {230 dir = ‘west‘;231 }232 else if (dir == ‘west‘)233 {234 dir = ‘north‘;235 }236 else if (dir == ‘north‘)237 {238 dir = ‘east‘;239 }240 241 graph.setCellStyles(mxConstants.STYLE_DIRECTION, dir, [cell]);242 }243 }244 }245 }246 }247 finally248 {249 graph.getModel().endUpdate();250 }251 }252 }, null, null, ‘Ctrl+R‘);253 254 //视图操作255 this.addAction(‘actualSize‘, function()256 {257 graph.zoomTo(1);258 });259 this.addAction(‘zoomIn‘, function() { graph.zoomIn(); }, null, null, ‘Add‘);260 this.addAction(‘zoomOut‘, function() { graph.zoomOut(); }, null, null, ‘Subtract‘);261 this.addAction(‘fitWindow‘, function() { graph.fit(); });262 263 this.addAction(‘fitPage‘, mxUtils.bind(this, function()264 {265 if (!graph.pageVisible)266 {267 this.get(‘pageView‘).funct();268 }269 var fmt = graph.pageFormat;270 var ps = graph.pageScale;271 var cw = graph.container.clientWidth - 20;272 var ch = graph.container.clientHeight - 20;273 274 var scale = Math.floor(100 * Math.min(cw / fmt.width / ps, ch / fmt.height / ps)) / 100;275 graph.zoomTo(scale);276 277 graph.container.scrollLeft = Math.round(graph.view.translate.x * scale - Math.max(10, (graph.container.clientWidth - fmt.width * ps * scale) / 2));278 graph.container.scrollTop = Math.round(graph.view.translate.y * scale - Math.max(10, (graph.container.clientHeight - fmt.height * ps * scale) / 2));279 }));280 this.addAction(‘fitPageWidth‘, mxUtils.bind(this, function()281 {282 if (!graph.pageVisible)283 {284 this.get(‘pageView‘).funct();285 }286 287 var fmt = graph.pageFormat;288 var ps = graph.pageScale;289 var cw = graph.container.clientWidth - 20;290 291 var scale = Math.floor(100 * cw / fmt.width / ps) / 100;292 graph.zoomTo(scale);293 294 graph.container.scrollLeft = Math.round(graph.view.translate.x * scale - Math.max(10, (graph.container.clientWidth - fmt.width * ps * scale) / 2));295 graph.container.scrollTop = Math.round(graph.view.translate.y * scale - Math.max(10, (graph.container.clientHeight - fmt.height * ps * scale) / 2));296 }));297 this.put(‘customZoom‘, new Action(mxResources.get(‘custom‘), function()298 {299 var value = http://www.mamicode.com/mxUtils.prompt(mxResources.get(‘enterValue‘) + ‘ (%)‘, parseInt(graph.getView().getScale() * 100));300 301 if (value != null && value.length > 0 && !isNaN(parseInt(value)))302 {303 graph.zoomTo(parseInt(value) / 100);304 }305 }));306 307 //选择操作308 var action = null;309 action = this.addAction(‘grid‘, function()310 {311 graph.setGridEnabled(!graph.isGridEnabled());312 editor.updateGraphComponents();313 }, null, null, ‘Ctrl+Shift+G‘);314 action.setToggleAction(true);315 action.setSelectedCallback(function() { return graph.isGridEnabled(); });316 action = this.addAction(‘guides‘, function() { graph.graphHandler.guidesEnabled = !graph.graphHandler.guidesEnabled; });317 action.setToggleAction(true);318 action.setSelectedCallback(function() { return graph.graphHandler.guidesEnabled; });319 action = this.addAction(‘tooltips‘, function()320 {321 graph.tooltipHandler.setEnabled(!graph.tooltipHandler.isEnabled());322 });323 action.setToggleAction(true);324 action.setSelectedCallback(function() { return graph.tooltipHandler.isEnabled(); });325 action = this.addAction(‘navigation‘, function()326 {327 graph.foldingEnabled = !graph.foldingEnabled;328 graph.view.revalidate();329 });330 action.setToggleAction(true);331 action.setSelectedCallback(function() { return graph.foldingEnabled; });332 action = this.addAction(‘scrollbars‘, function()333 {334 graph.scrollbars = !graph.scrollbars;335 editor.updateGraphComponents();336 337 if (!graph.scrollbars)338 {339 var t = graph.view.translate;340 graph.view.setTranslate(t.x - graph.container.scrollLeft / graph.view.scale, t.y - graph.container.scrollTop / graph.view.scale);341 graph.container.scrollLeft = 0;342 graph.container.scrollTop = 0;343 graph.sizeDidChange();344 }345 else346 {347 var dx = graph.view.translate.x;348 var dy = graph.view.translate.y;349 350 graph.view.translate.x = 0;351 graph.view.translate.y = 0;352 graph.sizeDidChange();353 graph.container.scrollLeft -= Math.round(dx * graph.view.scale);354 graph.container.scrollTop -= Math.round(dy * graph.view.scale);355 }356 }, !mxClient.IS_TOUCH);357 action.setToggleAction(true);358 action.setSelectedCallback(function() { return graph.container.style.overflow == ‘auto‘; });359 action = this.addAction(‘pageView‘, mxUtils.bind(this, function()360 {361 graph.pageVisible = !graph.pageVisible;362 graph.pageBreaksVisible = graph.pageVisible; 363 graph.preferPageSize = graph.pageBreaksVisible;364 graph.view.validate();365 graph.sizeDidChange();366 367 editor.updateGraphComponents();368 editor.outline.update();369 370 if (mxUtils.hasScrollbars(graph.container))371 {372 if (graph.pageVisible)373 {374 graph.container.scrollLeft -= 20;375 graph.container.scrollTop -= 20;376 }377 else378 {379 graph.container.scrollLeft += 20;380 graph.container.scrollTop += 20;381 }382 }383 }));384 action.setToggleAction(true);385 action.setSelectedCallback(function() { return graph.pageVisible; });386 this.put(‘pageBackgroundColor‘, new Action(mxResources.get(‘backgroundColor‘), function()387 {388 var apply = function(color)389 {390 graph.background = color;391 editor.updateGraphComponents();392 };393 394 var cd = new ColorDialog(ui, graph.background || ‘none‘, apply);395 ui.showDialog(cd.container, 220, 360, true, false);396 397 if (!mxClient.IS_TOUCH)398 {399 cd.colorInput.focus();400 }401 }));402 action = this.addAction(‘connect‘, function()403 {404 graph.setConnectable(!graph.connectionHandler.isEnabled());405 }, null, null, ‘Ctrl+Q‘);406 action.setToggleAction(true);407 action.setSelectedCallback(function() { return graph.connectionHandler.isEnabled(); });408 409 410 this.addAction(‘help‘, function()411 {412 var ext = ‘‘;413 414 if (mxResources.isLanguageSupported(mxClient.language))415 {416 ext = ‘_‘ + mxClient.language;417 }418 419 window.open(RESOURCES_PATH + ‘/help‘ + ext + ‘.html‘);420 });421 this.put(‘about‘, new Action(mxResources.get(‘about‘) + ‘ Graph Editor‘, function()422 {423 ui.showDialog(new AboutDialog(ui).container, 320, 280, true, true);424 }, null, null, ‘F1‘));425 426 //风格427 var toggleFontStyle = mxUtils.bind(this, function(key, style)428 {429 this.addAction(key, function()430 {431 graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE, style);432 });433 });434 435 toggleFontStyle(‘bold‘, mxConstants.FONT_BOLD);436 toggleFontStyle(‘italic‘, mxConstants.FONT_ITALIC);437 toggleFontStyle(‘underline‘, mxConstants.FONT_UNDERLINE);438 439 //颜色440 this.addAction(‘fontColor‘, function() { ui.menus.pickColor(mxConstants.STYLE_FONTCOLOR); });441 this.addAction(‘strokeColor‘, function() { ui.menus.pickColor(mxConstants.STYLE_STROKECOLOR); });442 this.addAction(‘fillColor‘, function() { ui.menus.pickColor(mxConstants.STYLE_FILLCOLOR); });443 this.addAction(‘gradientColor‘, function() { ui.menus.pickColor(mxConstants.STYLE_GRADIENTCOLOR); });444 this.addAction(‘backgroundColor‘, function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BACKGROUNDCOLOR); });445 this.addAction(‘borderColor‘, function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BORDERCOLOR); });446 447 // 格式448 this.addAction(‘shadow‘, function() { graph.toggleCellStyles(mxConstants.STYLE_SHADOW); });449 this.addAction(‘dashed‘, function() { graph.toggleCellStyles(mxConstants.STYLE_DASHED); });450 this.addAction(‘rounded‘, function() { graph.toggleCellStyles(mxConstants.STYLE_ROUNDED); });451 this.addAction(‘style‘, function()452 {453 var cells = graph.getSelectionCells();454 455 if (cells != null && cells.length > 0)456 {457 var model = graph.getModel();458 var style = mxUtils.prompt(mxResources.get(‘enterValue‘)+ ‘ (‘ + mxResources.get(‘style‘) + ‘)‘,459 model.getStyle(cells[0]) || ‘‘);460 461 if (style != null)462 {463 graph.setCellStyle(style, cells);464 }465 }466 });467 this.addAction(‘setAsDefaultEdge‘, function()468 {469 var cell = graph.getSelectionCell();470 471 if (cell != null && graph.getModel().isEdge(cell))472 {473 //目前采取的快照单元的调用474 var proto = graph.getModel().cloneCells([cell])[0];475 476 //删除条目- / exitXY风格477 var style = proto.getStyle();478 style = mxUtils.setStyle(style, mxConstants.STYLE_ENTRY_X, ‘‘);479 style = mxUtils.setStyle(style, mxConstants.STYLE_ENTRY_Y, ‘‘);480 style = mxUtils.setStyle(style, mxConstants.STYLE_EXIT_X, ‘‘);481 style = mxUtils.setStyle(style, mxConstants.STYLE_EXIT_Y, ‘‘);482 proto.setStyle(style);483 484 //使用连接的边缘模板预览485 graph.connectionHandler.createEdgeState = function(me)486 {487 return graph.view.createState(proto);488 };489 490 //从边缘模板创建新连接491 graph.connectionHandler.factoryMethod = function()492 {493 return graph.cloneCells([proto])[0];494 };495 }496 });497 this.addAction(‘image‘, function()498 {499 function updateImage(value, w, h)500 {501 var select = null;502 var cells = graph.getSelectionCells();503 504 graph.getModel().beginUpdate();505 try506 {507 //如果没有选中单元格插入新的细胞508 if (cells.length == 0)509 {510 var gs = graph.getGridSize();511 cells = [graph.insertVertex(graph.getDefaultParent(), null, ‘‘, gs, gs, w, h)];512 select = cells;513 }514 515 graph.setCellStyles(mxConstants.STYLE_IMAGE, value, cells);516 graph.setCellStyles(mxConstants.STYLE_SHAPE, ‘image‘, cells);517 518 if (graph.getSelectionCount() == 1)519 {520 if (w != null && h != null)521 {522 var cell = cells[0];523 var geo = graph.getModel().getGeometry(cell);524 525 if (geo != null)526 {527 geo = geo.clone();528 geo.width = w;529 geo.height = h;530 graph.getModel().setGeometry(cell, geo);531 }532 }533 }534 }535 finally536 {537 graph.getModel().endUpdate();538 }539 540 if (select != null)541 {542 graph.setSelectionCells(select);543 graph.scrollCellToVisible(select[0]);544 }545 };546 547 var value = http://www.mamicode.com/‘‘;548 var state = graph.getView().getState(graph.getSelectionCell());549 550 if (state != null)551 {552 value = http://www.mamicode.com/state.style[mxConstants.STYLE_IMAGE] || value;553 }554 555 value = http://www.mamicode.com/mxUtils.prompt(mxResources.get(‘enterValue‘) + ‘ (‘ + mxResources.get(‘url‘) + ‘)‘, value);556 557 if (value != null)558 {559 if (value.length > 0)560 {561 var img = new Image();562 563 img.onload = function()564 {565 updateImage(value, img.width, img.height);566 };567 img.onerror = function()568 {569 mxUtils.alert(mxResources.get(‘fileNotFound‘));570 };571 572 img.src =http://www.mamicode.com/ value;573 }574 }575 });576 };577 578 /**579 * 注册名字下行动。580 */581 Actions.prototype.addAction = function(key, funct, enabled, iconCls, shortcut)582 {583 return this.put(key, new Action(mxResources.get(key), funct, enabled, iconCls, shortcut));584 };585 586 /**587 *注册名字下行动。588 */589 Actions.prototype.put = function(name, action)590 {591 this.actions[name] = action;592 593 return action;594 };595 596 /**597 * 返回给定名称的行动或null如果没有这样的行动存在。598 */599 Actions.prototype.get = function(name)600 {601 return this.actions[name];602 };603 604 /**605 * 构造一个新的行动为给定的参数。606 */607 function Action(label, funct, enabled, iconCls, shortcut)608 {609 mxEventSource.call(this);610 this.label = label;611 this.funct = funct;612 this.enabled = (enabled != null) ? enabled : true;613 this.iconCls = iconCls;614 this.shortcut = shortcut;615 };616 617 //行动继承自mxEventSource618 mxUtils.extend(Action, mxEventSource);619 620 Action.prototype.setEnabled = function(value)621 {622 if (this.enabled != value)623 {624 this.enabled = value;625 this.fireEvent(new mxEventObject(‘stateChanged‘));626 }627 };628 629 Action.prototype.setToggleAction = function(value)630 {631 this.toggleAction = value;632 };633 634 Action.prototype.setSelectedCallback = function(funct)635 {636 this.selectedCallback = funct;637 };638 639 Action.prototype.isSelected = function()640 {641 return this.selectedCallback();642 };
进行坐标分配的XML文件集以及相应的图标
以一个数据库图标的坐标管理XML的部分数据为例database.xml
1 <shapes name="mxGraph.aws.database"> 2 <shape name="ElastiCache" h="56.81" w="55.7" aspect="variable" strokewidth="inherit"> 3 <connections> 4 <constraint x="0.5" y="0" perimeter="0" name="N"/> 5 <constraint x="0.5" y="1" perimeter="0" name="S"/> 6 <constraint x="0" y="0.5" perimeter="0" name="W"/> 7 <constraint x="1" y="0.5" perimeter="0" name="E"/> 8 <constraint x="0.025" y="0.025" perimeter="0" name="NW"/> 9 <constraint x="0.025" y="0.975" perimeter="0" name="SW"/>10 <constraint x="0.975" y="0.025" perimeter="0" name="NE"/>11 <constraint x="0.975" y="0.975" perimeter="0" name="SE"/>12 </connections>13 <background>14 <path>15 <move x="0" y="51.81"/>16 <curve x1="0" y1="54.57" x2="2.24" y2="56.81" x3="5" y3="56.81"/>17 <line x="50.7" y="56.81"/>18 <curve x1="53.46" y1="56.81" x2="55.7" y2="54.57" x3="55.7" y3="51.81"/>19 <line x="55.7" y="5"/>20 <curve x1="55.7" y1="2.24" x2="53.46" y2="0" x3="50.7" y3="0"/>21 <line x="5" y="0"/>22 <curve x1="2.24" y1="0" x2="0" y2="2.24" x3="0" y3="5"/>23 <line x="0" y="51.81"/>24 <close/>25 </path>26 </background>27 <foreground>28 <fillstroke/>
我写的这个SaveToXmlServlet.java文件的目的是将网络拓扑图保存至对应的XML文件中 以及 读取网络拓扑图对应的XML文件
1 package grapheditor; 2 import java.io.BufferedReader; 3 import java.io.File; 4 import java.io.FileReader; 5 import java.io.IOException; 6 import java.io.PrintWriter; 7 import java.io.RandomAccessFile; 8 import javax.servlet.ServletException; 9 import javax.servlet.http.HttpServlet;10 import javax.servlet.http.HttpServletRequest;11 import javax.servlet.http.HttpServletResponse;12 /**13 * 将网络拓扑图保存至对应的XML文件中 以及 读取网络拓扑图对应的XML文件14 * @author Visec·Dana15 * @version V2.0 2014-7-1716 */17 public class SaveToXmlServlet extends HttpServlet {18 private static final long serialVersionUID = 1L;19 public void doGet(HttpServletRequest request, HttpServletResponse response)20 throws ServletException, IOException {21 this.doPost(request, response);22 }23 public void doPost(HttpServletRequest request, HttpServletResponse response)24 throws ServletException, IOException {25 response.setContentType("text/html;charset=utf-8");26 response.setCharacterEncoding("utf-8");27 request.setCharacterEncoding("utf-8");28 String type = request.getParameter("type");29 String tp = request.getParameter("tp");30 StringBuffer result = new StringBuffer("");31 String xmlPath=new String("");32 String strPath = this.getClass().getResource("/").toString();33 xmlPath = ("qsy".equals(tp))?"network_map/network_qsy.xml":("dzj".equals(tp))?"network_map/network_dzj.xml":("zdw".equals(tp))?"network_map/network_zdw.xml":"network_map/network_sp.xml";34 String osName = System.getProperties().getProperty("os.name");35 if(osName.toLowerCase().indexOf("windows")>-1){36 strPath=strPath.substring(6)+xmlPath;37 }else{38 strPath=strPath.substring(5)+xmlPath;39 }40 File file = new File(strPath);41 if(file.isFile()){//判断该路径是否为一个文件42 if("set".equals(type.toLowerCase())){//文件保存43 String xml = request.getParameter("xml");44 if(xml==null||"".equals(xml)){45 result.append("0");46 }else{47 RandomAccessFile randomAccessFile = new RandomAccessFile(strPath, "rw");48 randomAccessFile.seek(0);49 randomAccessFile.setLength(0);50 randomAccessFile.write(xml.getBytes());51 randomAccessFile.close();52 result.append("1");53 }54 }else if("get".equals(type.toLowerCase())){//获取文件信息55 //开始读取56 BufferedReader reader = new BufferedReader(new FileReader(new File(strPath)));57 String tempString = null;58 // 一次读入一行,直到读入null为文件结束59 while ((tempString = reader.readLine()) != null){60 result.append(tempString);61 }62 reader.close();63 }64 }else{65 System.out.println(strPath+" 找不到!");66 result.append("0");67 }68 69 PrintWriter out = response.getWriter();70 out.write(result.toString());71 out.flush();72 out.close();73 }74 75 }
当然这个文件的基础是先前有绘制好的拓扑图已经保存了相应的坐标位置和相应的数据
我编写network_qsy.xml是用来存储相应的坐标的临时文件
文件配置,以及不同项目之间的嵌入都不一样,所有就不详细介绍了,如感兴趣的朋友欢加入群一起探讨更多相关技术提高自身水平!
当然部分网友可能留意到
这里部分功能是没有完善的,能力有限,还需一些时间来琢磨!
当然后期的开发是无穷的,后期也在此基础上添加了右键绑定相关设备,合一拖动的形式配置相关信息,
参考资料:http://www.yworks.com/en/products_yed_about.html
http://docs.cryengine.com/display/SDKDOC2/Flow+Graph+Editor
http://www.univ-orleans.fr/lifo/software/Agape/javadoc/agape/applications/GraphEditor.html
上诉网站都是英文版的当时,也只是略看一些资料!
基于Web实现在线绘画拓扑图[GraphEditor]