【问题标题】:Modifying node and edge properties in d3.js在 d3.js 中修改节点和边属性
【发布时间】:2013-02-16 06:05:07
【问题描述】:

我正在使用 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. 如果我确实想要抖动节点,我可以确定要抖动哪组节点吗? (这样我就可以通知用户哪个链接器节点的视觉外观发生了变化。)

已编辑

我暂时已经放弃了调整节点,但是下面的代码(基于@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(...);
}

如果在应用属性后图形仍在移动(如果是alpha() &gt; 0),我不需要调用start/stopresume,因为随后的刻度会拾取新属性。如果图形已经稳定,调用 start 会刷新它而不移动节点。

【问题讨论】:

    标签: d3.js force-layout


    【解决方案1】:
    1. 您是否尝试过在“更新节点”块之前调用force.stop(),在之后调用force.resume()

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

    【讨论】:

    • 我如何知道要修改哪些节点(我可能会使用class 属性而不是id)以使后续的.select() 调用工作?我假设我有一些数据绑定到一些 SVG 元素;数据发生了变化……我怎么知道要修改哪些元素?
    • 我只是说id 因为你已经有了一个我不想搞砸的qNode 类......如果你在数据上设置一些任意属性怎么办?它会改变 - 所以无论您更改或更新数据,添加如下内容:nodes.forEach(function(d, i) { d.changed = true }); - 然后我认为您可以稍后通过使用选择器功能进行常规 .select 调用来进行选择:nodes.select(function(d,i) { return d.changed == true ? this : null } - 请参阅@987654321 @了解更多关于过滤的信息
    • 感谢您对问题 #1 的回答:致电 force.start(); force.stop(); 正确更新了外观。至于调整已更改的节点,那是行不通的:当我将节点集和边限制为所选集时,它们被移动到图表的中间,而忽略了其他节点。我想我可能能够修复整个节点集,然后只加热我想要突出显示的节点,但这似乎过于复杂。我想我需要一个更好的解决方案。
    • 如果您使用整个选择,但根据绑定到节点的d.changed 变量过滤您的移动怎么办?我不知道你是如何调整节点的,但我这样做的方式是更改x 并让它们跳回静态x0(或yy0)。它看起来像nodes.attr("x", function(d) { return d.changed == true ? x+(Math.random()*10-5) : x });,然后对于 y 也是一样的。如果摆动令人困惑,请查看我的 this 源代码。
    • 哦,您还必须在更改 xy attr 后调用 force.resume()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-22
    • 2017-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多