拓扑图编辑器(1)
多位客户提到编辑器的需求,譬如拖拽创建节点,文本编辑,连线编辑,尺寸编辑等等,编辑是一个系列话题,将陆续作介绍,本章介绍拖拽创建节点与简单的连线编辑交互
拖拽创建节点
拖拽创建节点,以前要实现拖拽,需要监听全局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;
}
运行效果