采用d3开发流程设计器(三)添加普通任务节点

1、普通任务节点是流程里面的最常用的节点,这种节点是rect形状
需求:

  • 普通节点可以进行拖拽
  • 普通节点可以进行大小变化
  • 普通节点可以拖拽出线条,跟其他节点连接

第一次这么深入的玩svg和d3 还是走了蛮多冤枉路,废话不多说了,上效果图吧。

在这里插入图片描述

这里面涉及几个技巧
1、text节点和边框拖拽节点和节点rect要实现在一个g里头,这样拖拽事件给g即可,不需要每个元素去监听拖拽。
2、拖拽的时候,要对window.d3.event.x进行特殊的计算处理,因为rect节点的宽高都比较大,导致每次获取都x,y都是不统一的,如果简单的进行translate(window.d3.event.x,{window.d3.event.x},{window.d3.event.y})的话,会出现跳动的问题。
再就是采用了timer来使移动更加平滑。

 function dragstarted(d) {
        if (d != null) {
            if(desObj.drageTimer!=null){
                clearTimeout(desObj.drageTimer);
            }
            desObj.drageTimer=setTimeout(()=>{
                desObj.startedDrage=true;
            },100);
        }
    }

    function dragged(d) {
        if (d != null&&desObj.startedDrage) {
            desObj.vueMethods.hideNodeMenu&&desObj.vueMethods.hideNodeMenu();
            if(d.mvX1==null){
                d.mvX1=window.d3.event.x;
                d.mvY1=window.d3.event.y;
                return void(0);
            }

            d.mvX2=window.d3.event.x;
            d.mvY2=window.d3.event.y;

            let moveX=parseFloat(d.mvX2)-parseFloat(d.mvX1);
            let moveY=parseFloat(d.mvY2)-parseFloat(d.mvY1);
            d.moveX=moveX;
            d.moveY=moveY;
            window.d3.select(this).attr("transform",`translate(${parseFloat(d.fx)+parseFloat(moveX)},${parseFloat(d.fy)+parseFloat(moveY)})`);

            for(let drageType in d.linkDrage){
                let links=d.linkDrage[drageType];
                //如果启始存在
                if(links.startX!==null){
                    window.d3.select("#link_"+drageType+"_"+d.id) .attr("d", (d)=>{
                        return `M${ d.linkDrage[drageType]["cx"]+ d.fx} ${ d.linkDrage[drageType]["cy"]+ d.fy}L${d.linkDrage[drageType].endX} ${d.linkDrage[drageType].endY}`;
                    });
                }
            }
        }
    }

    function dragended(d) {
        if(d!=null){
            if(d.moveX!=null&&!isNaN(d.moveX)){
                d.fx=parseFloat(d.fx)+parseFloat(d.moveX);
                d.fy=parseFloat(d.fy)+parseFloat(d.moveY);
                d.moveX=null;
                d.moveY=null;
            }
            d.mvX1=null;
            d.mvY1=null;
            d.mvX2=null;
            d.mvY2=null;
            desObj.startedDrage=false;
            //如果存在timer 则需要清空
            if(desObj.drageTimer!=null){
                clearTimeout(desObj.drageTimer);
            }
        }
    }

3、计算text的宽度,再进行位置的计算

FlowDesCtl.prototype.addNodeText=function({node,nodeWidth,nodeText,colorText="#000",nodeHeight}){
    node.append("text")
        .attr("x", (d)=>{
            $(`<span id="labelTemp">${nodeText}</span>`).appendTo($('#' + this.containerId));
            let labelWid=$("#labelTemp").width();$("#labelTemp").remove();
            d.labelWid=labelWid+20;
            return parseFloat(nodeWidth)/2-labelWid/2;
        })
        .attr("y", parseFloat(nodeHeight/2)+5)
        .text(nodeText)
        .attr("stroke-width", "0.8px")
        .style('stroke',colorText)
        .attr("font-family", 'PingFangSC, "Microsoft YaHei", "Lantinghei SC", serif')
        .attr("font-size", 16)
        .style("text-anchor"," start")
        .on("click", (d) => {
            //左键盘点击 则隐藏右键菜单
            this.vueMethods.hideNodeMenu&&this.vueMethods.hideNodeMenu();
            //隐藏其他节点的边框拖拽
            this._hideDragSizeBorder();
            //显示当前节点的拖拽边框
            this._showCurDragSizeBorder(d);
            window.d3.event.stopPropagation();
        })
        .on("contextmenu", d => {
            this._showNodeMenu(d);
        });
};

4、绘制拖拽节点

FlowDesCtl.prototype._addDragSizeBorder=function({nodeCtl,nodeData,nodeWidth,nodeHeight,nodeType}){

    const borderNode = nodeCtl.append("g")
    // .selectAll("g")
        .data([nodeData])
        .attr("stroke-linecap", "round")
        .attr("stroke-linejoin", "round")
        .attr("class","drage_size_border")
        .attr("id", (d)=>{
            return "drage_size_border_"+d.id+"";
        })
        .style("cursor",`move)`)
        .attr("fill-opacity", 0)
        .attr("stroke-opacity", 0);
    

    let rectWidth=nodeWidth;
    let rectHeight=nodeHeight;
    if(nodeType==='startNode'||nodeType==='endNode'){
        rectWidth=nodeWidth*2;
        rectHeight=rectHeight*2;
    }

    let padding=14;

    borderNode.append("rect")
        .attr("rx", 0)
        .attr("ry", 0)
        .attr("id", (d)=>{
            return "rect_drage_"+d.id+"";
        })
        .attr("fill", "#fff")
        .attr("width", rectWidth+padding)
        .attr("height",  rectHeight+padding)
        .attr("stroke", "#dfdfdf")
        .attr("stroke-width", 1)
        .attr("transform",`translate(${-padding/2},${-padding/2})`)
        .attr("fill-opacity", 0)
        .attr("stroke-opacity", 0);

    //绘制top 第一个 top-left拖拽节点
    this._drawDragNode({borderNode,x:-padding/2-this.options.dragNodeWidth/2,y:-padding+this.options.dragNodeWidth/2,cursor:"nw-resize",dragType:"nw"});
    //绘制top 第二个 top-center拖拽节点
    this._drawDragNode({borderNode,x:this.options.taskNodeWidth/2,y:-padding+this.options.dragNodeWidth/2,cursor:"n-resize",dragType:"n"});
    //绘制top 第三个 top-right拖拽节点
    this._drawDragNode({borderNode,x:this.options.taskNodeWidth+this.options.dragNodeWidth/2,y:-padding+this.options.dragNodeWidth/2,cursor:"ne-resize",dragType:"ne"});


    //绘制center 第一个拖拽节点 center-left
    this._drawDragNode({borderNode,x:-padding/2-this.options.dragNodeWidth/2,y:this.options.taskNodeHeight/2-this.options.dragNodeWidth/2,cursor:"w-resize",dragType:"w"});
    //绘制center 第二个拖拽节点 center-right
    this._drawDragNode({borderNode,x:this.options.taskNodeWidth+this.options.dragNodeWidth/2,y:this.options.taskNodeHeight/2-this.options.dragNodeWidth/2,cursor:"e-resize",dragType:"e"});


    //绘制bottom 第一个 bottom-left拖拽节点
    this._drawDragNode({borderNode,x:-padding/2-this.options.dragNodeWidth/2,y:this.options.taskNodeHeight+this.options.dragNodeWidth/2,cursor:"sw-resize",dragType:"sw"});
    //绘制bottom 第二个 bottom-center拖拽节点
    this._drawDragNode({borderNode,x:this.options.taskNodeWidth/2,y:this.options.taskNodeHeight+this.options.dragNodeWidth/2,cursor:"s-resize",dragType:"s"});
    //绘制bottom 第三个 bottom-right拖拽节点
    this._drawDragNode({borderNode,x:this.options.taskNodeWidth+this.options.dragNodeWidth/2,y:this.options.taskNodeHeight+this.options.dragNodeWidth/2,cursor:"se-resize",dragType:"se"});
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章