採用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"});
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章