拓扑图编辑器(1)

原文:http://blog.qunee.com/?p=4443

多位客户提到编辑器的需求,譬如拖拽创建节点,文本编辑,连线编辑,尺寸编辑等等,编辑是一个系列话题,将陆续作介绍,本章介绍拖拽创建节点与简单的连线编辑交互

拖拽创建节点

拖拽创建节点,以前要实现拖拽,需要监听全局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;
}

运行效果

创建连线交互

在线演示

http://demo.qunee.com/editor/Editor.html


2 × 五 =