D3J使用调用更新错误的元素

时间:2013-12-26 23:56:13

标签: javascript d3.js

我是D3JS的新手,您可以在http://jsfiddle.net/3n8wD/

找到代码

我面临一个问题,任何指针都会有所帮助

当我移动线条时,它按预期分开,但是一旦我尝试移动圆圈,它就会跳回到线条。

在检查数组时,看起来圆圈数组正在更新,因为我正在移动链接,不知道是什么导致了这一点。

对此的任何帮助都将受到高度赞赏。下面是我在jsfiddle上的代码

var width = 960,
    height = 500;
graph1 = {"nodes":[{"x":444,"y":275},{"x":378,"y":324}],"links":[{"source":1,"target":0}]}


var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);
var onDraggable = function(d1) {
          console.log(d1);

          d1.x = d3.event.x, d1.y = d3.event.y;
          d3.select(this).attr("cx", d1.x).attr("cy", d1.y);
          //console.log(d1);
          link.filter(function(l) { return l.source === d1; }).attr("x1", d3.event.x).attr("y1", d3.event.y);
          link.filter(function(l) { return l.target === d1; }).attr("x2", d3.event.x).attr("y2", d3.event.y);


        }
var drag = d3.behavior.drag()
      .origin(function(d) { return d; })
      .on("drag", onDraggable)




  var onDraggable1 = function(d) {
          //d.x1 = d3.event.x1, d.y1 = d3.event.y1, d.x2=d3.event.x2, y2=d3.event.y2;
          var mouseX = d3.mouse(this)[0];
          var mouseY = d3.mouse(this)[1];
          var relativeX = mouseX-d.source.x;
          var relativeY = mouseY-d.source.y;
          console.log(d);

          //console.log(d);
         // d3.select(this).attr("x1", d3.event.x).attr("y1", d3.event.y).attr("x2", d3.event.x).attr("y2", d3.event.y);
          d.source.x= d.source.x+relativeX;
          d.source.y = d.source.y+relativeY;
          d.target.x= d.target.x+relativeX;
          d.target.y = d.target.y+relativeY;

          d3.select(this).attr("x1", d.source.x).attr("y1", d.source.y);
          d3.select(this).attr("x2", d.target.x).attr("y2", d.target.y);

        }
var drag1 = d3.behavior.drag()
      .origin(function(d) { return d; })
      .on("drag", onDraggable1);

  graph1.links.forEach(function(d) {
    d.source = graph1.nodes[d.source];
    d.target = graph1.nodes[d.target];
  });




var node = svg.append("g").attr("class","node")
    .selectAll("circle")
      .data(graph1.nodes)
    .enter().append("circle")
      .attr("r", 4)
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })
      .call(drag);

var link = svg.append("g").attr("class","link")
    .selectAll("line")
      .data(graph1.links)
    .enter().append("line")
      .attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; })
      .attr("stroke-width", 2)
      .attr("stroke", "black")
      .call(drag1);

1 个答案:

答案 0 :(得分:2)

您的节点“跳转”到行尾的原因,即使该行已被移走,也是因为该节点的数据对象 相同 < / strong> object作为行的源/目标数据对象。节点的d值和链接的d.sourced.target值只是指向浏览器内存中同一对象的指针(引用)。您对该数据对象所做的一切都会反映在链接和节点中。这就是节点拖动功能的作用:直接更改数据对象,然后更新直线和圆的位置以匹配已经更改的数据值。

因此,即使您在移动线时没有更新圆的cxcy位置,行拖动方法中的语句d.source.x =等也会设置节点的d.xd.y值。然后在节点拖动方法中,当您访问这些值以确定新位置时,相对于线的末端而不是屏幕上圆圈的位置确定移动。

那么如何获得所需的行为,将行与节点分开?当您开始拖动它时,您需要为该行的源和目标创建一个 new 数据对象,该对象不再引用节点的数据对象。您只需要在拖动开始时执行一次,因此您可以使用拖动行为对象的dragstart event

var drag1 = d3.behavior.drag()
      .origin(function(d) { return d; })
      .on("dragstart", separateNodes);
      .on("drag", onDraggable1);

separateNodes方法将会很短。我们所要做的就是,对于链接的目标和源,创建一个新的数据对象,它是现有目标/源对象的副本(但可以独立于原始对象进行编辑)。 Javascript没有复制对象的任何默认方法(虽然various extensions do),但如果有问题的对象只包含x和y属性,那么很容易做到。

var separateNodes = function(d) {
    //create new data objects that are copies of d.source and d.target
    var newSource = {x:d.source.x, y:d.source.y};
    var newTarget = {x:d.target.x, y:d.target.y};

    //set the new objects as the target/source of this link
    d.source = newSource; 
    d.target = newTarget;
}

你甚至可以跳过变量声明,只需将它们全部合并为一行代码,另一行代表目标,但我发现这样更容易理解。但是,我应该提一下,这种方法是有效的,因为x和y都只是简单的数据类型(数字),而不是指向对象的指针。因此,当它显示x:d.source.x时,您实际上正在获取该数字的新副本,您可以更改该数字,而不会影响包含该数字的原始对象。

但是,如果您的节点和链接也具有复杂数据对象的value属性,那么当该行与节点分离时,您必须决定要执行的操作:是否要创建整个值数据对象的副本,或维护到原始对象的链接,或者用null(空/未定义)对象替换它?第一种情况很复杂,并且取决于值对象的数据结构;对于第二个你不需要做任何事情(除了记住你有多个指向同一个对象的指针);对于最后一种情况,您只需在value:null,函数中的newSourcenewTarget对象定义中添加separateNodes属性。

希望一切都有意义,
--ABR