2015-10-14 60 views
1

我有一个d3图表,利用Force Layout。当我在前面有一组节点时,它可以很好地展现出来。具体而言,我的意思是节点保持很好的分离,链接正确。添加一个节点没有正确链接到现有节点

我的问题演示在jsFiddle here

我还包括下面的代码片段,似乎像jsFiddle一样工作。

但是,如果我start一个节点,然后添加其他打倒Add Person按钮行了,你会发现第一个节点(即使它在该链接的引用)没有回应,也不能移动。

看来后者是真正的问题,因为它不能被移动。

我已经试过

  1. 添加一个新的人到图形时,在顶部执行一个force.resume(),而不是force...start()结果:这导致错误,Error: Invalid value for <g> attribute transform="translate(NaN,NaN)"发生,我还没有找出原因。
  2. 在该方法的末尾添加force.resume(),并且而不是在将新人添加到图时再次添加on('tick' ...。我通过在顶部执行force...start()来实现这一点,而不管resume结果:导致第一个节点再次反弹(有希望),但添加的节点停留在左上角,就好像它没有连接。

var scope = {}; 
 

 
scope.nodes = []; 
 
scope.links = []; 
 

 
var width = 960, 
 
    height = 500; 
 

 
var svg = d3.select("body").append("svg") 
 
    .attr("width", width) 
 
    .attr("height", height); 
 

 
var force = d3.layout.force() 
 
    .charge(-150) 
 
    .linkDistance(150) 
 
    .size([width, height]); 
 

 
function renderGraph(resume) { 
 
    force.nodes(scope.nodes) 
 
     .links(scope.links) 
 
     .start(); 
 

 
    var link = svg.selectAll(".link") 
 
     .data(scope.links) 
 
     .enter().append("line") 
 
     .attr("class", "link"); 
 

 
    var node = svg.selectAll(".node") 
 
     .data(scope.nodes) 
 
     .enter().append("g") 
 
     .attr("class", "node") 
 
     .call(force.drag); 
 

 
    node.append("image") 
 
     .attr("xlink:href", function (d) { 
 
     return d.avatar || 'https://github.com/favicon.ico' 
 
    }) 
 
     .attr("x", -56) 
 
     .attr("y", -8) 
 
     .attr("width", 64) 
 
     .attr("height", 64); 
 

 
    node.append("text") 
 
     .attr("dx", 12) 
 
     .attr("dy", ".35em") 
 
     .text(function (d) { 
 
     return d._id === scope.user.profile._id ? 'You' : d.firstName + ' ' + d.lastName 
 
    }); 
 

 
    force.on("tick", function() { 
 
     link.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; 
 
     }); 
 

 
     node.attr("transform", function (d) { 
 
      return "translate(" + d.x + "," + d.y + ")"; 
 
     }); 
 
    }); 
 
} 
 

 
scope.user = { 
 
    profile: { 
 
     _id: 1, 
 
     firstName: 'Bob', 
 
     lastName: 'Smith' 
 
    } 
 
}; 
 
scope.nodes.push(scope.user.profile); 
 
renderGraph(); 
 

 
var b = document.getElementById("addButton"); 
 
b.onclick = addPerson; 
 

 
function addPerson() { 
 
    scope.nodes.push({ 
 
     _id: 2, 
 
     firstName: 'Jane', 
 
     lastName: 'Smith' 
 
    }); 
 
    scope.links.push({ 
 
     source: 0, 
 
     target: scope.nodes.length - 1 
 
    }); 
 
    renderGraph(); 
 
}
.link { 
 
    stroke: #ccc; 
 
} 
 
.node text { 
 
    pointer-events: none; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<button id="addButton">Add Person</button>

回答

1

这里的问题是:

当你

var node = svg.selectAll(".node") 
     .data(scope.nodes) 
     .enter().append("g") 
     .attr("class", "node") 
     .call(force.drag); 

的节点g组件和蜱期望选择即:

var node = svg.selectAll(".node") 
     .data(scope.nodes); 

它应该是:

var link = svg.selectAll(".link") 
    .data(scope.links);//this selection is expected in the tick function 

link.enter().append("line") 
    .attr("class", "link"); 

var node = svg.selectAll(".node") 
    .data(scope.nodes);//this selection is expected in the tick function 

//attaching text/circle everything pertaining to the node n the g group. 
var nodeg = node.enter().append("g") 
    .attr("class", "node") 
    .call(force.drag); 

工作代码here

希望这有助于!

1

我是有点慢,但我会后我的答案呢,因为它设置了一点点不同,以接受的答案...

无需每次重新连接阵列给力布局,使我把它移到了渲染函数之外,我还通过删除已删除的对象来增加了良好的内务管理,但除此之外,添加的内容并不多。

var scope = {}; 
 

 
scope.nodes = []; 
 
scope.links = []; 
 

 
var width = 600, 
 
    height = 190; 
 

 
var svg = d3.select("body").append("svg") 
 
    .attr("width", width) 
 
    .attr("height", height); 
 

 
var force = d3.layout.force() 
 
    .charge(-150) 
 
    .linkDistance(150) 
 
    .size([width, height]) 
 
\t .nodes(scope.nodes) 
 
    .links(scope.links) 
 
     ; 
 

 
function renderGraph(resume) { 
 
    force 
 
     .start(); 
 

 
    var link = svg.selectAll(".link") 
 
     .data(scope.links); 
 
    link.enter().append("line") 
 
     .attr("class", "link"); 
 
    link.exit().remove(); 
 

 
    var node = svg.selectAll(".node") 
 
     .data(scope.nodes), 
 
    \t newNode = node.enter().append("g") 
 
     .attr("class", "node") 
 
     .call(force.drag); 
 
    node.exit().remove(); 
 

 
    newNode.append("image") 
 
     .attr("xlink:href", function (d) { 
 
     return d.avatar || 'https://github.com/favicon.ico' 
 
    }) 
 
     .attr("x", -56) 
 
     .attr("y", -8) 
 
     .attr("width", 64) 
 
     .attr("height", 64); 
 

 
    newNode.append("text") 
 
     .attr("dx", 12) 
 
     .attr("dy", ".35em") 
 
     .text(function (d) { 
 
     return d._id === scope.user.profile._id ? 'You' : d.firstName + ' ' + d.lastName 
 
    }); 
 

 
    force.on("tick", function() { 
 
     link.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; 
 
     }); 
 

 
     node.attr("transform", function (d) { 
 
      return "translate(" + d.x + "," + d.y + ")"; 
 
     }); 
 
    }); 
 
} 
 

 
scope.user = { 
 
    profile: { 
 
     _id: 1, 
 
     firstName: 'Bob', 
 
     lastName: 'Smith' 
 
    } 
 
}; 
 
scope.nodes.push(scope.user.profile); 
 
renderGraph(); 
 

 
var b = document.getElementById("addButton"); 
 
b.onclick = addPerson; 
 

 
function addPerson() { 
 
    scope.nodes.push({ 
 
     _id: 2, 
 
     firstName: 'Jane', 
 
     lastName: 'Smith' 
 
    }); 
 
    scope.links.push({ 
 
     source: 0, 
 
     target: scope.nodes.length - 1 
 
    }); 
 
    renderGraph(); 
 
}
.link { 
 
    stroke: #ccc; 
 
} 
 
.node text { 
 
    pointer-events: none; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<button id="addButton">Add Person</button>