javascript - d3js 在转换组内移动 SVG 元素

标签 javascript d3.js svg

我正在开发一个查询生成器项目。我正在尝试使用 d3.js 构建一个查询生成器。我被困在一个部分,我想在转换组内移动某些元素。这是repo我被困在这个function 。我想在连接后移动运算符(operator)并更新连接的线路。谁能帮我?

var circleDrag = d3.behavior.drag()
    .on('dragstart', function () {
        d3.event.sourceEvent.stopPropagation();

    })
    .on('drag', function () {
        var parentQboxGroupId = d3.select(this).select(function () {
            return this.parentNode;
        });
        var grandParent = parentQboxGroupId.select(function(){
            return this.parentNode;
        });

        var drawingGroup = d3.select('#'+grandParent.attr('id'));
        var currentC = d3.select(this);

            dragging = true;
            drawingGroup
            .select('.lineInsideQbox')
            .attr('x1', currentC.attr('cx'))
            .attr('y1', currentC.attr('cy'))
            .style('stroke','green')
            .style('stroke-width','2px');

        dummyLine.src = currentC.attr('id');
        console.log('CIRCLE IS BEING DRAGGED' + JSON.stringify(dummyLine));


    })
    .on('dragend', function () {
        console.log('drag circle end');

        //if(!selectedCircle.id){
        //    dummyLine.target = selectedQbox.id;
        //}
        dummyLine.target = selectedCircle.id;
        dragging = false;

        console.log('DRAG END : SELCTED NODE : '+ JSON.stringify(selectedCircle));
        console.log('DRAG END : DUMMY LINE : '+ JSON.stringify(dummyLine));

        var targetNode = d3.select('#'+dummyLine.target);
        var srcNode = d3.select('#'+dummyLine.src);
        console.log('split : ' + dummyLine.src.split('--'));

        var group = '#' + (dummyLine.src).split('--')[1];
        console.log('G: ' + group);
        d3.select(group).append('line')
            .attr('id', function () {


                var a = (dummyLine.src).split('--');
                var b = (dummyLine.target).split('--');
                if( a[0]== 'nodeRight'){
                    return dummyLine.src + '__' + dummyLine.target;
                }else{
                    return dummyLine.target + '__' + dummyLine.src;
                }

            })
            .attr('class', function () {
                var a = (dummyLine.src).split('--');
                var b = (dummyLine.target).split('--');
                return 'line '+ a[1]+' '+b[1];
            })
            .attr('x1', srcNode.attr('cx'))
            .attr('y1',srcNode.attr('cy'))
            .attr('x2',targetNode.attr('cx'))
            .attr('y2',targetNode.attr('cy'))
            .style('stroke', 'black')
            .style('stroke-width', '3px')
        ;
        dummyLine.src = null;
        dummyLine.target = null;
    });

编辑:当我尝试删除查询框时。我可以将其他运算符(operator)放入其中。然后我应该能够在里面连接它们。这是显示我正在尝试的图像。

enter image description here

连接完成后,我尝试分别移动大盒子和小运算符(operator)。这就是代码中断的地方。

最佳答案

主要问题是要移动操作符,您需要使用平移来移动整个组(标签),其中包括图像、两个圆圈和线条。然后,您可以使用所连接的其他运营商的 CX、CY 值来设置线路的另一端。 这不起作用,因为当您执行平移时,圆的 CX 和 CY 值不会更新,因此在第二次移动时,它将把 x、y 值放在圆的原始点,而不是移动的点。 要解决这个问题,不要平移整个组,而是仅平移图像,更新圆的 cx 和 cy 值,然后使用圆的新 cx、cy 更新行 x、y 值:

所需的所有修改都位于您的operatorDrag.js 文件中。 首先,当您追加圆圈时,添加一个保存原始 cx 和 cy 值的属性。当拖动操作符时计算新的 cx, cy 时我们将需要这些:

从此更改:

 var op = currGroup
            .append('image')
            .attr('class', 'operator')
            .attr('width', elem.attr('width') * 0.75)
            .attr('height', elem.attr('height') * 0.75)
            .attr('x', d3.mouse(this)[0])
            .attr('y', d3.mouse(this)[1])
            .attr('xlink:href', elem.attr('href'));

        currGroup
            .append('circle')
            .attr('class', 'node nodeLeft')
            .attr('id', function () {
                return 'nodeLeft--' + currGroup.attr('id');
            })
            .attr('cx', op.attr('x'))
            .attr('cy', op.attr('height') / 2 + Number(op.attr('y')))
            .attr('r', 5)
            .style('fill', 'red')
            .on('mouseover', function () {
                selectedCircle = {
                    id: d3.select(this).attr('id'),
                    cls: 'nodeLeft'
                }
            })
            .call(circleDrag)
        ;

        currGroup
            .append('circle')
            .attr('class', 'node nodeRight')
            .attr('id', function () {
                return 'nodeRight--' + currGroup.attr('id');
            })
            .attr('cx', Number(op.attr('x')) + Number(op.attr('width')))
            .attr('cy', op.attr('height') / 2 + Number(op.attr('y')))
            .attr('r', 5)
            .style('fill', 'red')

            .on('mouseover', function () {
                selectedCircle = {
                    id: d3.select(this).attr('id'),
                    cls: 'nodeRight'
                }
            })
            .call(circleDrag)

        ;

为此(更新的代码包含在以#SB 开头的注释中):

     var op = currGroup
                    .append('image')
                    .attr('class', 'operator')
                    .attr('width', elem.attr('width') * 0.75)
                    .attr('height', elem.attr('height') * 0.75)
                    .attr('x', d3.mouse(this)[0])
                    .attr('y', d3.mouse(this)[1])
                    .attr('xlink:href', elem.attr('href'));

                currGroup
                    .append('circle')
                    .attr('class', 'node nodeLeft')
                    .attr('id', function () {
                        return 'nodeLeft--' + currGroup.attr('id');
                    })
                    .attr('cx', op.attr('x'))
                    .attr('cy', op.attr('height') / 2 + Number(op.attr('y')))

                // #SB: add a reference to the original cx and cy position.
                // we will need it to set new cx cy when moving operator

                    .attr('data-cx', op.attr('x'))
                    .attr('data-cy', op.attr('height') / 2 + Number(op.attr('y')))
               //----------------------------------------------------------------------
                    .attr('r', 5)
                    .style('fill', 'red')
                    .on('mouseover', function () {
                        selectedCircle = {
                            id: d3.select(this).attr('id'),
                            cls: 'nodeLeft'
                        }
                    })
                    .call(circleDrag)
                ;

                currGroup
                    .append('circle')
                    .attr('class', 'node nodeRight')
                    .attr('id', function () {
                        return 'nodeRight--' + currGroup.attr('id');
                    })
                    .attr('cx', Number(op.attr('x')) + Number(op.attr('width')))
                    .attr('cy', op.attr('height') / 2 + Number(op.attr('y')))
                // #SB: add a reference to the original cx and cy position.
                // we will need it to set new cx cy when moving operator

                    .attr('data-cx', Number(op.attr('x')) + Number(op.attr('width')))
                    .attr('data-cy', op.attr('height') / 2 + Number(op.attr('y')))
               //----------------------------------------------------------------------
                    .attr('r', 5)
                    .style('fill', 'red')

                    .on('mouseover', function () {
                        selectedCircle = {
                            id: d3.select(this).attr('id'),
                            cls: 'nodeRight'
                        }
                    })
                    .call(circleDrag)

                ;

完成此操作后,转到运算符的拖动方法。这是我们要更改的代码:

               .on('drag', function () {

                    var g = d3.select(this);

                    var currentOp = g.select('.operator');
                    var parent = g.select(function () {
                        return this.parentNode;
                    }).select('.qbox');


                    var dx = d3.event.x;
                    var dy = d3.event.y;

                    var mouse = {dx: d3.event.x, dy: d3.event.y};
                    var currentObj = {
                        x: currentOp.attr('x'),
                        y: currentOp.attr('y'),
                        width: currentOp.attr('width'),
                        height: currentOp.attr('height')
                    };
                    var parentObj = {
                        x: parent.attr('x'),
                        y: parent.attr('y'),
                        width: parent.attr('width'),
                        height: parent.attr('height')
                    };


                    //console.log('parent width : ' + parent.attr('width'));
                    //console.log('parent width : ' + currentOp.attr('width'));
                    //g.attr('transform', 'translate(' + x + ',' + y + ')');
                    var loc = getXY(mouse, currentObj, parentObj);
                    g.attr('transform', 'translate(' + loc.x + ',' + loc.y + ')');


                    d3.select('#' + g.attr('id')).selectAll('.line')[0].forEach(function (e1) {

                        var line = d3.select(e1);
                        console.log('-------------------');
                        console.log('line : ' + line.attr('id'));
                        console.log('-------------------');
                        var split = line.attr('id').split('__');
                        if(g.attr('id') == split[0]){
                            //change x2, y2
                            var otherNode = d3.select('#'+split[1]);
                            line.attr('x2', otherNode.attr('cx'));
                            line.attr('y2', otherNode.attr('cy'));
                        }else{
                            var otherNode = d3.select('#'+split[0]);
                            line.attr('x1', otherNode.attr('cx'));
                            line.attr('y1', otherNode.attr('cy'));
                        }


                    })


                }))

首先,不要翻译整个对象,仅翻译图像:

              var g = d3.select(this);

                    var currentOp = g.select('.operator');

                    var parent = g.select(function () {
                        return this.parentNode;
                    }).select('.qbox');

                    //#SB: added a reference to the parent id
                    var parent_id = g.select(function () {
                        return this.parentNode;
                    }).attr('id');

                    //---------------------------------------

                    var dx = d3.event.x;
                    var dy = d3.event.y;

                    var mouse = {dx: d3.event.x, dy: d3.event.y};
                    var currentObj = {
                        x: currentOp.attr('x'),
                        y: currentOp.attr('y'),
                        width: currentOp.attr('width'),
                        height: currentOp.attr('height')
                    };
                    var parentObj = {
                        x: parent.attr('x'),
                        y: parent.attr('y'),
                        width: parent.attr('width'),
                        height: parent.attr('height')
                    };



                    var loc = getXY(mouse, currentObj, parentObj);


                    //#SB: Do not translate everything, the cx, cy values of the circle are not updated
                    // when translating which will make future moves calculate incorrectly
                    g.selectAll('image').attr('transform', 'translate(' + loc.x + ',' + loc.y + ')');

然后,不要平移圆圈,而是使用原始 cx、cy 和平移值更改其 cx 和 cy 值:

                   g.selectAll('circle')
                    .attr('cx', function () {

                        return parseFloat(d3.select(this).attr('data-cx')) + parseFloat(loc.x);
                    })
                    .attr('cy', function () {

                        return parseFloat(d3.select(this).attr('data-cy')) + parseFloat(loc.y);
                    });

最后一件事是线路的更新。在原始代码中,您选择了运算符组中的所有行,但仅选择该组实际上会错过一些行。某些线路可以是另一个运算符(operator)组的一部分,但会连接到正在移动的运算符(operator)。 在这种情况下,我们应该选择父组内的所有线路,并检查该线路是否连接到我们正在移动的运算符(operator)。 如果已连接,则我们更新 x 和 y 值:

//#SB: Select all the lines in the parent group instead of only group of the
                        // operator we are moving. There can be lines that exists on other groups that 
                        // do not exist within the group that is being moved. 


                        d3.select('#' + parent_id).selectAll('.line')[0].forEach(function (el) {

var parent_id = g.attr('id')
                            var line = d3.select(el)
                            var nodeType = line.attr('id').split("__");  // id tells us if the line is connected to the left or right node
                            var operators = line.attr('class').split(" ");  // class holds info on what operators the line is connected to
                            var sourceCircleId = nodeType[0].split("--")[0] + '--' + operators[1];
                            var targetCircleId = nodeType[1].split("--")[0] + '--' + operators[2]; 

                           if (parent_id == operators[1] || parent_id == operators[2]) {  // the line is connected to the operator we are moving 

                                line.attr('x1', d3.select('#' + sourceCircleId).attr('cx'))
                                line.attr('y1', d3.select('#' + sourceCircleId).attr('cy'))
                                line.attr('x2', d3.select('#' + targetCircleId).attr('cx'))
                                line.attr('y2', d3.select('#' + targetCircleId).attr('cy'))

                            }

                        });

完整的OnDrag代码:

             .on('drag', function () {

                    var g = d3.select(this);

                    var currentOp = g.select('.operator');

                    var parent = g.select(function () {
                        return this.parentNode;
                    }).select('.qbox');

                    //#SB: added a reference to the parent id
                    var parent_id = g.select(function () {
                        return this.parentNode;
                    }).attr('id');

                    //---------------------------------------

                    var dx = d3.event.x;
                    var dy = d3.event.y;

                    var mouse = {dx: d3.event.x, dy: d3.event.y};
                    var currentObj = {
                        x: currentOp.attr('x'),
                        y: currentOp.attr('y'),
                        width: currentOp.attr('width'),
                        height: currentOp.attr('height')
                    };
                    var parentObj = {
                        x: parent.attr('x'),
                        y: parent.attr('y'),
                        width: parent.attr('width'),
                        height: parent.attr('height')
                    };



                    var loc = getXY(mouse, currentObj, parentObj);


                    //#SB: Do not translate everything, the cx, cy values of the circle are not updated
                    // when translating which will make future moves calculate incorrectly
                    g.selectAll('image').attr('transform', 'translate(' + loc.x + ',' + loc.y + ')');

                    g.selectAll('circle')
                    .attr('cx', function () {

                        return parseFloat(d3.select(this).attr('data-cx')) + parseFloat(loc.x);
                    })
                    .attr('cy', function () {

                        return parseFloat(d3.select(this).attr('data-cy')) + parseFloat(loc.y);
                    });


                    //#SB: Select all the lines in the parent group instead of only group of the
                    // operator we are moving. There can be lines that exists on other groups that 
                    // do not exist within the group that is being moved. 


                    d3.select('#' + parent_id).selectAll('.line')[0].forEach(function (el) {

 var parent_id = g.attr('id')
                            var line = d3.select(el)
                            var nodeType = line.attr('id').split("__");  // id tells us if the line is connected to the left or right node
                            var operators = line.attr('class').split(" ");  // class holds info on what operators the line is connected to
                            var sourceCircleId = nodeType[0].split("--")[0] + '--' + operators[1];
                            var targetCircleId = nodeType[1].split("--")[0] + '--' + operators[2];  

                       if (parent_id == operators[1] || parent_id == operators[2]) {  // the line is connected to the operator we are moving 

                            line.attr('x1', d3.select('#' + sourceCircleId).attr('cx'))
                            line.attr('y1', d3.select('#' + sourceCircleId).attr('cy'))
                            line.attr('x2', d3.select('#' + targetCircleId).attr('cx'))
                            line.attr('y2', d3.select('#' + targetCircleId).attr('cy'))

                        }

                    });



                }))

关于javascript - d3js 在转换组内移动 SVG 元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31935084/

相关文章:

javascript - D3 自动完成将div更改为输入框

javascript - 如何在 d3.js 中有选择地缩放 x 和/或 y - 动态?

html - 不使用 CSS 或 JS 创建 SVG 饼图的数学方法

javascript - 使用 D3.js 投影显示 map

javascript - nodejs cookie 不会从 request.post 持续到后续 request.get 调用

javascript - 名称 id 或类中的正则表达式?

javascript - STOMP Web 套接字回调不起作用

javascript - 递归地(或迭代地)用 d3.js 制作一个嵌套的 html 表?

javascript - AJAX header 中的希伯来字符

javascript - 通过 jQuery 切换使用 SVG 图标