我使用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()
等,
- 如何修改图表的视觉外观没有任何轻摇节点?
- 如果我确实想要晃动节点,我可以确定哪一组节点要晃动吗? (所以,我可以通知其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
/stop
或resume
如果应用属性后图形仍然在移动(如果alpha() > 0
),因为随后的刻度将拾取新的属性。如果图形已经确定,调用开始将刷新它而不移动节点。
如何知道要修改哪些节点(我可能使用'class'属性而不是'id')来使后续的.select()调用起作用?我假设我有一些数据绑定到一些SVG元素;数据发生变化......我如何知道要修改哪些元素? – 2013-02-18 23:09:06
我只是说'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
感谢您回答问题#1:调用'force.start(); force.stop();'正确更新了外观。至于调整已经改变的节点,那是行不通的:当我将节点和边的集合限制到选定的集合时,它们被移动到图表的中间,忽略了其他节点。我想我可能会修复整个节点集,然后加热那些我想突出显示的节点,但这看起来过于复杂。我想我需要一个更好的解决方案。 – 2013-02-19 07:06:14