简体   繁体   English

当mousedown和mouseup发生在不同的元素上时,如何处理D3拖动事件?

[英]How to handle D3 drag events when mousedown and mouseup happen on different elements?

I'm experiencing problems with the d3 drag behavior. 我在使用d3拖动行为时遇到问题。 Variations of the issue I'm having have been answered before , but I couldn't find an answer to my specific problem. 之前已经回答过各种问题 ,但是我找不到针对自己特定问题的答案。

Here's a fiddle which illustrates the problem I'm going to describe. 这是一个小提琴 ,它说明了我将要描述的问题。

What I want to do is to have a click handler on a draggable element where the click handler shouldn't be executed on dragend . 我想要做的是在可拖动元素上具有单击处理程序,其中不应在dragend上执行单击处理程序。 I know that inside the click handler I can use d3.event.defaultPrevented which should be set to true if the element was dragged. 我知道在点击处理程序中我可以使用d3.event.defaultPrevented ,如果拖动了元素,则应将其设置为true。 The problem arises when the mouseup event happens on another element than the mousedown event. mouseup事件发生在mousedown事件以外的其他元素上时,就会出现问题。 This happens when the movement of the dragged element is slower than the mouse cursor. 当拖动的元素的移动速度比鼠标光标慢时,就会发生这种情况。 If the mouse is released and the dragged element isn't under the mouse yet d3.event.defaultPrevented is set to false and the click handler doesn't get called. 如果释放鼠标并且拖动的元素不在鼠标d3.event.defaultPreventedd3.event.defaultPrevented设置为false,并且不会调用单击处理程序。 This makes it impossible to find out whether the click event was fired after a drag or not. 这使得无法确定在拖动后是否触发了click事件。

In my example the circle flashes green if the click handler executes but d3.event.defaultPrevented was set to true. 在我的示例中,如果单击处理程序执行但d3.event.defaultPrevented设置为true,则圆圈闪烁绿色。 Also in the click handler propagation is prevented preventing the event to bubble up to the svg click handler. 同样,在单击处理程序中,可以防止传播,从而防止事件冒泡到svg单击处理程序。 If the click handler of the circle doesn't execute and the event bubbles to the svg click handler the circle flashes blue if d3.event.defaultPrevented was set to true otherwise it flashes red. 如果未执行圆圈的单击处理程序,并且事件冒泡到svg单击处理程序,则如果d3.event.defaultPrevented设置为true,则圆圈将闪烁蓝色,否则将闪烁红色。

What I want to achieve is to get the circle to flash green or blue no matter where the circle is on mouse up in order to be able to know whether the click event happened after a drag or not. 我想要实现的是使圆圈在鼠标上方的任何位置都闪烁绿色或蓝色,以便能够知道单击事件是否在拖动之后发生。 Is this even possible or is this a limitation by the nature of javascript/browsers? 这是否有可能,或者这是JavaScript /浏览器的本质限制吗? If so is there a workaround? 如果是这样,是否有解决方法? Or do I just have to disable the 'slowing down' of the circle when it is dragged? 还是只需要在拖动圆时禁用圆的“减速”?

I found a very similar question on SO but there wasn't really a useful answer. 我在SO上发现了一个非常类似的问题 ,但实际上并没有一个有用的答案。

Any help appreciated! 任何帮助表示赞赏!

EDIT Looks like the idea to stop the element from slowing down during dragging solves the problem. 编辑看起来像是在拖动过程中阻止元素减速的想法解决了这个问题。 But I would still be interested if this is possible using the event information available. 但是我仍然会对使用事件信息可以实现这一点感兴趣。

Here's the code of the fiddle: 这是小提琴的代码:

var nodes = [{}];
var svg = d3.select('body')
        .append('svg')
        .attr({
            width: 500,
            height: 500
        })
        .on('click', function(){
            var color = d3.event.defaultPrevented ? 'blue' : 'red';
            flashNode(color);
        });

var force = d3.layout.force()
        .size([500, 500])
        .nodes(nodes)
        .friction(.2)
        .on('tick', forceTickHandler);

var nodeElements = svg
        .selectAll('circle');

nodeElements = nodeElements
        .data(force.nodes())
        .enter()
        .append('circle')
        .attr({
            cx: 10,
            cy: 10,
            r: 10
        })
        .on('click', function(){
            d3.event.stopPropagation();
            var color = d3.event.defaultPrevented ? 'green' : 'orange';
            flashNode(color);
        })
        .call(force.drag);

function forceTickHandler(e){
    nodes.forEach(function(node) {
      var k = e.alpha * 1.4;

      node.x += (250 - node.x) * k;
      node.y += (250 - node.y) * k;
    });

  nodeElements
  .attr('cx', function(d, i){
      return d.x;
  })
  .attr('cy', function(d, i){
      return d.y;
  });
};

function flashNode(color){
    nodeElements
        .attr('fill', color)
        .transition()
        .duration(1000)
        .attr('fill', 'black');
}

force.start();

The issue seems to be coming from the code in your forceTickHandler that updates the node positions: 问题似乎来自于forceTickHandler中更新节点位置的代码:

nodes.forEach(function(node) {
  var k = e.alpha * 1.4;

  node.x += (250 - node.x) * k;
  node.y += (250 - node.y) * k;
});

When this is commented out, the position of the node does not lag the mouse pointer. 当注释掉它时, node的位置不会滞后于鼠标指针。 I don't really understand what you want to happen with the code above. 我不太明白上面的代码会发生什么。 The "typical" way of doing this, is similar to the sticky force layout example at http://bl.ocks.org/mbostock/3750558 “典型”方法类似于http://bl.ocks.org/mbostock/3750558上的粘力布局示例

Update: Here's a way that might get you close to what you are after: https://jsfiddle.net/ktbe7yh4/3/ 更新:这是一种使您接近所追求的方式: https : //jsfiddle.net/ktbe7yh4/3/

I've created a new drag handler from force.drag and then updated what happens on dragend , it seems to achieve the desired effect. 我从force.drag创建了一个新的拖动处理程序,然后更新了dragend发生的事情,这似乎达到了预期的效果。

The code changes are the creation of the drag handler: 代码更改是拖动处理程序的创建:

var drag = force.drag()
                .on("dragend", function(d) {
                    flashNode('green');
                });

And then updating the creation of the nodes to use the new handler: 然后更新节点的创建以使用新的处理程序:

nodeElements = nodeElements
        .data(force.nodes())
        .enter()
        .append('circle')
        .attr({
            cx: 10,
            cy: 10,
            r: 10
        })
        .on('click', function(){
            d3.event.stopPropagation();
            var color = d3.event.defaultPrevented ? 'green' : 'orange';
            flashNode(color);
        })
        .call(drag);

The dragend in the drag handler gets called no matter what, but it still suffers from a similar problem that you describe, but you can deal with it a little better in the handler. 拖动处理程序中的dragend不管如何都会被调用,但是它仍然遇到您描述的类似问题,但是您可以在处理程序中更好地处理它。 To see what I mean here, try changing: 要了解我的意思,请尝试更改:

flashNode('green');

to: 至:

flashNode(d3.event.defaultPrevented ? 'green' : 'orange');

and you'll see that if you release the mouse when the pointer is directly pointing at the circle, it'll flash green. 并且您会看到,如果在指针直接指向圆圈时释放鼠标,它将闪烁绿色。 If the circle is lagging the pointer and you release the mouse before the circle has settled under the cursor, then it'll flash orange. 如果圆圈滞后于指针,并且您在圆圈停留在光标下方之前释放了鼠标,则它将闪烁橙色。 Having said that, the data element in the dragend handler always seems to be set to the circle that was dragged to begin with, whether the mouse button was released whilst pointing at the circle or not. 话虽如此,无论是否在指向圆圈的同时释放了鼠标按钮, dragend处理程序中的数据元素似乎总是设置为开始拖动的圆圈。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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