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

时间:2015-03-17 19:19:17

标签: javascript svg d3.js javascript-events

我遇到了d3拖动行为的问题。我遇到的问题的变化是answered before,但我无法找到我特定问题的答案。

Here's a fiddle说明了我要描述的问题。

我想要做的是在可拖动元素上设置一个点击处理程序,其中点击处理程序不应该在dragend上执行。我知道在click处理程序中我可以使用d3.event.defaultPrevented,如果拖动了元素,则应该将其设置为true。当mouseup事件发生在除mousedown事件之外的其他元素上时,会出现问题。当拖动元素的移动比鼠标光标慢时,会发生这种情况。如果释放鼠标并且拖动的元素不在鼠标下,则d3.event.defaultPrevented设置为false并且单击处理程序不会被调用。这使得无法确定在拖动之后是否触发了click事件。

在我的示例中,如果单击处理程序执行但是d3.event.defaultPrevented设置为true,则圆圈会闪烁绿色。同样在点击处理程序中,阻止传播阻止事件冒泡到svg单击处理程序。如果圆圈的点击处理程序没有执行,并且事件冒泡到svg点击处理程序,则如果d3.event.defaultPrevented设置为true,则圆圈闪烁蓝色,否则闪烁红色。

我想要达到的目的是让圆圈闪烁绿色或蓝色,无论圆圈在鼠标的哪个位置,以便能够知道在拖动之后是否发生了点击事件。这是否可能或者这是javascript /浏览器性质的限制?如果有的话有解决方法吗?或者我只是禁用“减速”'它被拖动时的圆圈?

我在SO上发现了一个非常相似的question,但它并没有真正有用的答案。

任何帮助表示赞赏!

修改 看起来在拖动过程中阻止元素减速的想法解决了问题。但如果可以使用现有的事件信息,我仍然会感兴趣。

这里是小提琴的代码:

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();

1 个答案:

答案 0 :(得分:0)

问题似乎来自forceTickHandler中更新节点位置的代码:

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

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

当注释掉时,node的位置不会滞后鼠标指针。我真的不明白你想要用上面的代码发生什么。 "典型"这样做的方式类似于http://bl.ocks.org/mbostock/3750558

处的粘性力布局示例

更新:这是一种可以让您接近所追求的方式:https://jsfiddle.net/ktbe7yh4/3/

我已经从force.drag创建了一个新的拖动处理程序,然后更新了dragend上发生的事情,它似乎达到了预期的效果。

代码更改是创建拖动处理程序:

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

然后更新节点的创建以使用新的处理程序:

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);

无论如何都会调用拖动处理程序中的dragend,但它仍然会遇到您描述的类似问题,但您可以在处理程序中更好地处理它。要了解我的意思,请尝试更改:

flashNode('green');

为:

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

并且您将看到如果在指针直接指向圆圈时松开鼠标,它将闪烁绿色。如果圆圈落在指针上,并且在圆圈固定在光标下方之前释放鼠标,则它会闪烁橙色。话虽如此,dragend处理程序中的数据元素似乎总是被设置为开始时拖动的圆圈,鼠标按钮是否在指向圆圈时被释放。