2013-02-16 201 views
2

我使用d3.js来使用强制布局布置节点和链接图。节点由圆圈表示;按行连结。在某些情况下,我想改变线或节点的视觉特征(例如,颜色,大小,不透明度等)以表示图上的一些状态改变。我已经能够通过重新绘制图表来做到这一点,但是这会抖动所有节点,导致混淆而不是清晰。修改d3.js中的节点和边属性

代码来创建图表:

force = d3.layout.force() 
     .charge(-120) 
     .gravity(0.2) 
     .linkDistance(30) 
     .size([width-pad, height-pad]); 


nodeSet = svg.selectAll(".qNode"); 
// BIND NODE DATA 
nodeSet = nodeSet.data(chartData.nodes); 
// CREATE NODES 
nodeSet.enter().append("circle") 
    .attr("class", "qNode") 
    .attr("r", function(d) { return d.size();}) 
    .style('stroke-opacity', function(d) { return d.opacity(); }) 
    .style('stroke', function(d) { return d.color(); }) 
    .style("fill", function(d) { return color(1); }); 
//similarly for links. 

force.nodes(chartData.nodes).links(chartData.links).start(); 

要更新图表,我用这个片段:

// SELECT NODES 
    nodeSet = svg.selectAll('.qNode'); 
    // JOIN NODES 
    nodeSet = nodeSet.data(force.nodes()); 
    // UPDATE NODES 
    nodeSet.attr("class", "qNode") 
    .attr("r", function(d) { return d.size();}) 
    .style("fill", function(d) { return color(1); }) 
    .style('stroke', function(d) { return d.color(); }) 
    .style('stroke-opacity', function(d) { return d.opacity(); }) 
    .style('opacity', 1) 
    .call(force.drag); 
    // CREATE NODES 
    nodeSet.enter().append("circle") 
    .attr("class", "qNode") 
    .attr("r", function(d) { return d.size();}) 
    .style("fill", function(d) { return color(1); }) 
    .call(force.drag); 
    // DELETE NODES 
    nodeSet.exit().remove(); 


    // START SHOW 
    force.start(); 

当这个运行时,整个图形摇晃之前那么新的笔触属性都有点应用。

所以我有两个问题:假设在数据对象的状态的变化会为d.size()返回不同的值,d.color()等,

  1. 如何修改图表的视觉外观没有任何轻摇节点?
  2. 如果我确实想要晃动节点,我可以确定哪一组节点要晃动吗? (所以,我可以通知其linkor节点有自己的视觉外观变化的用户。)

EDITED

我已经放弃了对轻摇节点为是的时间,但下面的代码(基于由@defenestrated建议),似乎做的伎俩更新一些节点的属性和图中边:

var allLinks = ... // my links from a d3 selectAll 
var allNodes = ... // my nodes from a d3 selectAll 
force = ... // my d3 force layout 
function updateGraph(graph, nodeSubset, linkSubset) { 
    for (var i=0; i<allLlinks.length; i++) 
     allLinks[i].selected = false; 
    for (var i=0; i<allNodes.length; i++) 
     allNodes[i].selected = false; 
    for (var i=0; i<linkSubset.length; i++) 
     linkSubset[i].selected = true; 
    for (var i=0; i<nodeSubset.length; i++) 
     nodeSubset[i].selected = true; 

    // these functions modify the selected nodes and links 
    linkSubset.call(setLinkAttributes); 
    nodeSubset.call(setNodeAttributes); 

    if (force.alpha() == 0) { 
     force.start(); 
     force.stop(); 
    } 
} 

function setLinkAttributes(links) { 
    link.style(...); 
} 

function setNodeAttributes(nodes) { 
    nodes.style(...); 
} 

我不需要调用start/stopresume如果应用属性后图形仍然在移动(如果alpha() > 0),因为随后的刻度将拾取新的属性。如果图形已经确定,调用开始将刷新它而不移动节点。

回答

1
  1. 有你打过电话force.stop()“更新节点”块之前,和force.resume()后?

  2. 你可以根据它们的状态对它们进行分类 - 所以你可以将.attr("id", "changed")应用到正在更新的节点上,然后在d3选择中使用它,例如。改变节点= d3.select("#changed")

+0

如何知道要修改哪些节点(我可能使用'class'属性而不是'id')来使后续的.select()调用起作用?我假设我有一些数据绑定到一些SVG元素;数据发生变化......我如何知道要修改哪些元素? – 2013-02-18 23:09:06

+0

我只是说'id',因为你已经有了一个'qNode'类,我不想搞砸......如果你在数据改变的时候对数据设置了一些任意的属性会怎么样 - 所以无论你在哪里改变或更新数据,添加如下内容:'nodes.forEach(function(d,i){d.changed = true});' - 那么我想你可以稍后通过定期调用'.select'来进行选择使用选择器函数:'nodes.select(function(d,i){return d.changed == true?this:null}' - 参见[this](https:// github。com/mbostock/d3/wiki/Selections#wiki-filter)获取更多关于过滤的信息 – defenestrated 2013-02-19 01:42:09

+0

感谢您回答问题#1:调用'force.start(); force.stop();'正确更新了外观。至于调整已经改变的节点,那是行不通的:当我将节点和边的集合限制到选定的集合时,它们被移动到图表的中间,忽略了其他节点。我想我可能会修复整个节点集,然后加热那些我想突出显示的节点,但这看起来过于复杂。我想我需要一个更好的解决方案。 – 2013-02-19 07:06:14