简体   繁体   中英

d3js preventing drag on click event

I am trying to draw a UI in d3js where I have a top panel and I should be able drag and drop elements to a group and copy them. I have achieved that goal, but there is a bug in my code. What I actually want to do is, drag the circle and copy it. But when I click the circle in the top panel it automatically triggers the drag event and copy it self. How can I stop this behaviour?

<svg height="600" width="600" style="background: black">

    <g>
        <rect x="0" y="0" , width="600" height="40" style="fill:gold;"></rect>
        <circle id='drag' cx="15" cy="20" init-cx="15" init-cy="20" r="10"
                style="stroke: white; stroke-width: 2px; fill:blue"/>

    </g>

    <g id="playground">
        <g>
            <circle id='top' cx="180" cy="120" r="30" style="stroke: white; stroke-width: 2px; fill:white"/>
        </g>
        <g>
            <circle id='top' cx="200" cy="220" r="30" style="stroke: white; stroke-width: 2px; fill:white"/>
        </g>
        <g>
            <circle id='top' cx="320" cy="150" r="50" style="stroke: white; stroke-width: 2px; fill:white"/>
        </g>
    </g>
</svg>

<script>

    $(document).ready(function () {


        var move = d3.behavior.drag()
            .on('drag', function () {

                console.log('dragging');

                var curr = d3.select(this)
                    .attr({
                        cx: d3.mouse(this)[0],
                        cy: d3.mouse(this)[1]
                    })


            })
            .on('dragend', function () {

                var curr = d3.select(this);

                d3.select('#playground')
                    .append('circle')
                    .attr({
                        cx : curr.attr('cx'),
                        cy : curr.attr('cy'),
                        r : curr.attr('r')
                    })
                    .style({
                        fill : 'white',
                        stroke : 'red',
                        'stroke-width' : '2px'
                    })
                ;

                curr.attr({
                    cx : curr.attr('init-cx'),
                    cy : curr.attr('init-cx')
                });
            })
            ;


        d3.select('#drag').call(move);


    });


</script>

here is the fiddle of my work. https://jsfiddle.net/fawzan/my2g724L/

You just need to check the target of drag-end event is the same circle and copy the circle only if they does not match.

.on('dragend', function() {
    if (d3.event.sourceEvent.target != this) {
      var curr = d3.select(this);

      d3.select('#playground')
        .append('circle')
        .attr({
          cx: curr.attr('cx'),
          cy: curr.attr('cy'),
          r: curr.attr('r')
        })
        .style({
          fill: 'white',
          stroke: 'red',
          'stroke-width': '2px'
        });

      curr.attr({
        cx: curr.attr('init-cx'),
        cy: curr.attr('init-cx')
      });
    }
});

 var move = d3.behavior.drag() .on('drag', function() { console.log('dragging'); console.log(this) console.log(this) var curr = d3.select(this) .attr({ cx: d3.mouse(this)[0], cy: d3.mouse(this)[1] }) }) .on('dragend', function() { if (d3.event.sourceEvent.target != this) { var curr = d3.select(this); d3.select('#playground') .append('circle') .attr({ cx: curr.attr('cx'), cy: curr.attr('cy'), r: curr.attr('r') }) .style({ fill: 'white', stroke: 'red', 'stroke-width': '2px' }); curr.attr({ cx: curr.attr('init-cx'), cy: curr.attr('init-cx') }); } }); d3.select('#drag').call(move); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <svg height="600" width="600" style="background: black"> <g> <rect x="0" y="0" , width="600" height="40" style="fill:gold;"></rect> <circle id='drag' cx="15" cy="20" init-cx="15" init-cy="20" r="10" style="stroke: white; stroke-width: 2px; fill:blue" /> </g> <g id="playground"> <g> <circle id='top' cx="180" cy="120" r="30" style="stroke: white; stroke-width: 2px; fill:white" /> </g> <g> <circle id='top' cx="200" cy="220" r="30" style="stroke: white; stroke-width: 2px; fill:white" /> </g> <g> <circle id='top' cx="320" cy="150" r="50" style="stroke: white; stroke-width: 2px; fill:white" /> </g> </g> </svg> 

Hope this will be useful for you, Have a look. I've added little code in 'dragend', that is here I'm deciding whether to create/append a circle to playground or not, by using circle's attributes like init-cx and init-cy. The code I've added is

var initX = (curr.attr('init-cx')*1);
var currX = (curr.attr('cx')*1);
var initY = (curr.attr('init-cy')*1);
var currY = (curr.attr('cy')*1);                  
if(((currX) > (initX+20)) || ((currY) > (initY+20))){
//Here code to append a circle to playground
}

Fiddle

:D

see your curr.attr('cx') offset is returning 15 when clicked.So you can prevent the default action by adding condition on dragend method:

if(curr.attr('cx')==15){
   return false;
}

More generic:

var isClicked = false;
var move = d3.behavior.drag()
    .on('dragstart', function() {
        isClicked = true;
    })
    .on('drag', function() {
        isClicked = false;

        var curr = d3.select(this)
            .attr({
                cx: d3.mouse(this)[0],
                cy: d3.mouse(this)[1]
            })
    })
    .on('dragend', function() {
        var curr = d3.select(this);
        if (!isClicked) {
            d3.select('#playground')
                .append('circle')
                .attr({
                    cx: curr.attr('cx'),
                    cy: curr.attr('cy'),
                    r: curr.attr('r')
                })
                .style({
                    fill: 'white',
                    stroke: 'red',
                    'stroke-width': '2px'
                });
        }

        curr.attr({
            cx: curr.attr('init-cx'),
            cy: curr.attr('init-cx')
        });
    });


d3.select('#drag').call(move);
d3.select('#drag').on('click', click);
function click(d) {
    isClicked = true;
}

check out Fiddle:

More Info

Use boolean to monitor whether the drag is initiated or not. In this way you can stop dragend function.

$(document).ready(function () {

    var isDragged = false;
    var move = d3.behavior.drag()
        .on('drag', function () {

            console.log('dragging');
            isDragged = true;
            var curr = d3.select(this)
                .attr({
                    cx: d3.mouse(this)[0],
                    cy: d3.mouse(this)[1]
                })


        })
        .on('dragend', function () {
            if (!isDragged) return;
            isDragged = false;
            var curr = d3.select(this);
            d3.select('#playground')
                .append('circle')
                .attr({
                    cx : curr.attr('cx'),
                    cy : curr.attr('cy'),
                    r : curr.attr('r')
                })
                .style({
                    fill : 'white',
                    stroke : 'red',
                    'stroke-width' : '2px'
                })
            ;

            curr.attr({
                cx : curr.attr('init-cx'),
                cy : curr.attr('init-cx')
            });
        })
        ;


    d3.select('#drag').call(move);


});

For D3 v4 (might be applicable to v5 and v6), you can determine the position of the node during the dragged event and see if that differs from its original position. Assuming it doesn't, alphaTarget is never called.

For example:

function dragstarted(d) {
    // if (!d3.event.active) simulation.alphaTarget(0.2).restart();
    d.fx = d.x;
    d.fy = d.y;
}

function dragged(d) {
    if (d3.event.active && (d.fy != d3.event.y || d.fx != d3.event.x)) {
        simulation.alphaTarget(0.5).restart();
    }

    d.fx = d3.event.x;
    d.fy = d3.event.y;
}


function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM