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

Javascript 当mousedown和mouseup发生在不同的元素上时,如何处理D3拖动事件?,javascript,d3.js,svg,dom-events,Javascript,D3.js,Svg,Dom Events,我遇到d3拖动行为的问题。我遇到的问题有很多种,但我找不到具体问题的答案 这说明了我要描述的问题 我想做的是在一个可拖动的元素上有一个click处理程序,其中click处理程序不应该在dragend上执行。我知道,在click处理程序中,我可以使用d3.event.defaultPrevented,如果拖动元素,则应将其设置为true。当mouseup事件发生在mousedown事件之外的另一个元素上时,就会出现问题。当拖动的元素的移动速度慢于鼠标光标时,会发生这种情况。如果释放鼠标并且拖动的元

我遇到d3拖动行为的问题。我遇到的问题有很多种,但我找不到具体问题的答案

这说明了我要描述的问题

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

在我的示例中,如果单击处理程序执行,但
d3.event.defaultPrevented
设置为true,则圆圈闪烁绿色。此外,在单击处理程序中,阻止了传播,防止事件冒泡到svg单击处理程序。如果圆圈的click处理程序未执行,且事件在svg click处理程序中出现气泡,则圆圈将闪烁蓝色(如果
d3.event.defaultPrevented
设置为true),否则将闪烁红色

我想要实现的是让圆圈闪烁绿色或蓝色,无论圆圈在鼠标上方的什么位置,以便能够知道拖动后是否发生单击事件。这是可能的,还是javascript/浏览器的特性限制了这一点?如果有,是否有解决办法?或者,我是否只需要在拖动圆时禁用它的“减速”

我发现了一个非常相似的答案,但是没有一个真正有用的答案

感谢您的帮助

编辑 看起来,在拖动过程中阻止元素减速的想法解决了问题。但我仍然很感兴趣,如果这是可能的使用事件信息可用

这是小提琴的代码:

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

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

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

  node.x += (250 - node.x) * k;
  node.y += (250 - node.y) * k;
});
注释掉后,
节点的位置不会滞后于鼠标指针。我真的不明白你想用上面的代码做什么。执行此操作的“典型”方式类似于

更新:这里有一种方法可以让你接近你想要的东西:

我从
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');
致:


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

感谢您的回答!我知道问题源于
勾选
处理程序和位置重新计算的滞后。我想要实现的是,当
dragend
事件发生时,即使鼠标光标不再位于元素上,也要触发click处理程序。但现在我知道我是这样写的,这听起来有点不可能和愚蠢。我想在拖动元素时,我将不得不跳过位置的重新计算。我的想法是在画布和元素上进行一些类似但额外的交互。我用一种可能的方式更新了我的答案,以实现您所追求的结果。这是一个有点变通的方法,但它可能会让你保持你的节点移动的方式,你所追求的闪存行为。
flashNode(d3.event.defaultPrevented ? 'green' : 'orange');