多位客户提到编辑器的需求,譬如拖拽创建节点,文本编辑,连线编辑,尺寸编辑等等,编辑是一个系列话题,将陆续作介绍,本章介绍拖拽创建节点与简单的连线编辑交互
拖拽创建节点
拖拽创建节点,以前要实现拖拽,需要监听全局mousemove事件,复制拖拽元素,实现drop效果,现在HTML5提供了新的拖拽事件,处理起来变得很容易
创建画布面板canvas,并在左侧创建一个工具箱toolbox,设置canvas监听drop和dragover事件,工具箱中的图片监听dragstart事件,并设置为可拖拽
<div id="canvas" ondrop="drop(event)" ondragover="allowDrop(event)"></div> <div id="toolbox"> <img src="node_icon.png" title="拖拽创建节点" draggable="true" ondragstart="drag(event)" image="node" type="Node" label="Node" /> </div>
拖拽事件处理
按如下方式处理拖拽事件,通过dataTransfer传递拖拽数据,并在drop时,创建新的节点
function allowDrop(ev) { ev.preventDefault(); } function drag(ev) { ev.dataTransfer.setData("image", ev.target.getAttribute("image")); ev.dataTransfer.setData("type", ev.target.getAttribute("type")); ev.dataTransfer.setData("label", ev.target.getAttribute("label")); } function drop(ev) { ev.preventDefault(); var image = ev.dataTransfer.getData("image"); var type = ev.dataTransfer.getData("type"); var label = ev.dataTransfer.getData("label"); var xy = graph.globalToLocal(ev); xy = graph.toLogical(xy.x, xy.y); if(type == "Node"){ var node = graph.createNode(label, xy.x, xy.y); if(image){ if(image.indexOf(".") < 0){ image = Q.Graphs[image]; } node.image = image; } } }
注意鼠标事件转换成Qunee逻辑坐标的代码:
var xy = graph.globalToLocal(ev);//鼠标事件转换成组件坐标
xy = graph.toLogical(xy.x, xy.y);//转换成逻辑坐标
创建连线交互
创建连线交互需要扩展交互模式,监听Qunee组件提供的drag监听,实现鼠标拖拽创建连线功能,并在交互画布上绘制连线轨迹
新建交互类CreateEdgeInteraction,并通过Q.Defaults.registerInteractions注册新的交互模式
var CREATE_EDGE_MODE = "create.edge.mode"; function CreateEdgeInteraction(graph){ this.graph = graph; this.topCavans = graph.topCanvas; } CreateEdgeInteraction.prototype = { destroy: function(graph){ this.start = null; graph.cursor = null; if(this.drawLineId){ this.topCavans.removeDrawable(this.drawLineId); delete this.drawLineId; this.topCavans.invalidate(); } }, drawLine: function(g, scale){ var x = this.start.x; var y = this.start.y; g.moveTo(x, y); if(this.edgeClass == FlexEdgeUI){ var cx = (this.start.x + this.end.x) / 2; var cy = (this.start.y + this.end.y) / 2; g.bezierCurveTo(this.start.x, this.start.y, cx, this.end.y, this.end.x, this.end.y); }else{ g.lineTo(this.end.x, this.end.y); } g.lineWidth = 3; g.strokeStyle = "#88F"; g.stroke(); }, invalidate: function(){ this.topCavans.invalidate(); }, startdrag: function(evt, graph){ var start = evt.getData(); if(!(start instanceof Q.Node)){ return; } evt.responded = true; this.start = start; graph.cursor = "crosshair"; this.drawLineId = this.topCavans.addDrawable(this.drawLine, this).id; }, ondrag: function(evt, graph){ if(!this.start){ return; } Q.stopEvent(evt); this.end = graph.getLogicalPointByMouseEvent(evt); this.invalidate(); }, // edgeClass: FlexEdgeUI, enddrag: function(evt, graph){ if(!this.start){ return; } graph.cursor = ""; this.invalidate(); var end = graph.getElementByMouseEvent(evt); if(end){ var edge = graph.createEdge(this.start, end); if(this.edgeClass){ edge.uiClass = this.edgeClass; } } this.destroy(graph); } } Q.Defaults.registerInteractions(CREATE_EDGE_MODE, [CreateEdgeInteraction, Q.PanInteraction]);
使用交互模式
增加一个切换按钮,实现默认交互模式与创建连线模式的切换
<img style="cursor: pointer;" title="创建连线" onclick="onCreateEdgeButtonClick(event)" alt="" src="edge_icon.png" />
切换交互模式
function onCreateEdgeButtonClick(evt){ var target = evt.target; if(target.selected){ target.selected = false; target.className = ""; }else{ target.selected = true; target.className = "selected"; } graph.interactionMode = target.selected ? CREATE_EDGE_MODE : Q.Consts.INTERACTION_MODE_DEFAULT; }
运行效果

huojiaqi
在线演示的例子节点无法拖动。请问有这个例子的源码包吗?
sam
修复了,这个demo已经整合到:http://demo.qunee.com/#Editor%20Demo
微笑
有一个问题想咨询下,graph的创建位置应该放在哪里?我放到drop()函数中,结果是只能拖拽一个组件;如果放到函数外作为全局变量,结果是创建的graph大小为0,虽然可以拖拽两次,但无法呈现在画布上。请问,graph应该放在什么地方进行创建?
谢谢!
sam
可以在dom加载完成后初始化graph,如果你使用jquery的话,可以用下面的代码
var graph;
$(function(){
graph = new Q.Graph(...);
...
})
微笑
这样虽然可以生成不为0的graph的画布,但是没有办法拖拽组件。下面是代码,您帮忙看下问题在哪儿?
拓扑编辑器
var graph;
function init(){
graph = new Q.Graph(“canvas”);
alert(” graph.width = ” + graph.width);
}
// var graph = new Q.Graph(“canvas”);
// alert(” graph.width = ” + graph.width);
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData(“type”, ev.target.getAttribute(“type”));
ev.dataTransfer.setData(“label”, ev.target.getAttribute(“label”));
ev.dataTransfer.setData(“src”, ev.target.getAttribute(“src”));
}
function drop(ev) {
ev.preventDefault();
var type = ev.dataTransfer.getData(“type”);
var label = ev.dataTransfer.getData(“label”);
var src = ev.dataTransfer.getData(“src”);
// graph = new Q.Graph(“canvas”);
// alert(graph.width);
var xy = graph.globalToLocal(ev);
xy = graph.toLogical(xy.x, xy.y);
if(type == “Node”){
var node = graph.createNode(null,xy.x – graph.width / 2, xy.y – graph.height / 2);
if(src){
node.image = src;
}
}
}
sam
设置graph为可编辑试一试
graph.editable = true;
微笑
这样的结果是:当拖拽组件时,组件不会被拖拽到canvas中,而是在一个独立页面中以图片的形式显示出来。就像http://demo.qunee.com/editor/Editor.html示例中一样,不同的是示例中是以一个新的页面呈现,在我本地是直接替换了原页面。
hhfwn
你好,qunee在拖拽一段时间后,就会出现 qunee for html5,无法编辑或者选中。请问这是什么问题呢
admin
license问题,试用版本仅限本机(localhost, 127.0.0,1)发布使用,请购买正式授权给,非商业用途授权请申请免费授权
梅康
如何保存拓扑图编辑器编辑好了的拓扑图。谢谢!
sam
可以通过JSONSerializer.js导出成json数据