在D3力导向图中突出显示所选节点,其链接及其子节点

时间:2012-01-05 07:33:20

标签: javascript d3.js highlight force-layout chord

我正在研究D3中的力导向图。我想通过将所有其他节点和链接设置为较低的不透明度来突出显示mouseover'd节点,其链接及其子节点。

在这个例子中,http://jsfiddle.net/xReHA/,我能够淡出所有的链接和节点然后淡化连接的链接,但到目前为止,我还没能优雅地淡化连接的节点这是当前鼠标悬停节点的子节点。

这是代码中的关键功能:

function fade(opacity) {
    return function(d, i) {
        //fade all elements
        svg.selectAll("circle, line").style("opacity", opacity);

        var associated_links = svg.selectAll("line").filter(function(d) {
            return d.source.index == i || d.target.index == i;
        }).each(function(dLink, iLink) {
            //unfade links and nodes connected to the current node
            d3.select(this).style("opacity", 1);
            //THE FOLLOWING CAUSES: Uncaught TypeError: Cannot call method 'setProperty' of undefined
            d3.select(dLink.source).style("opacity", 1);
            d3.select(dLink.target).style("opacity", 1);
        });
    };
}

当我尝试在从source.target加载的元素上设置不透明度时,我收到Uncaught TypeError: Cannot call method 'setProperty' of undefined错误。我怀疑这不是将该节点作为d3对象加载的正确方法,但我找不到另一种方法来加载它而不再遍历所有节点以找到与链接的目标或源匹配的节点。为了保持合理的性能,我不希望迭代所有节点超过必要的。

我举了淡化http://mbostock.github.com/d3/ex/chord.html的链接的例子:

enter image description here

但是,这并未显示如何更改已连接的子节点。

关于如何解决或改善这一问题的任何好建议都会被激烈地推翻:)

1 个答案:

答案 0 :(得分:88)

错误是因为您选择的是数据对象(d.source和d.target),而不是与这些数据对象关联的DOM元素。

你有突出显示的工作,但我可能会将你的代码组合成一个迭代,如下所示:

 link.style("opacity", function(o) {
   return o.source === d || o.target === d ? 1 : opacity;
 });

突出显示相邻节点更难,因为您需要知道每个节点的邻居。使用您当前的数据结构并不容易确定此信息,因为您拥有的所有信息都是一组节点和一组链接。忘记DOM一秒钟,并问问自己如何确定两个节点ab是否是邻居?

function neighboring(a, b) {
  // ???
}

一种昂贵的方法是迭代所有链接,看看是否有连接a和b的链接:

function neighboring(a, b) {
  return links.some(function(d) {
    return (d.source === a && d.target === b)
        || (d.source === b && d.target === a);
  });
}

(这假定链接是无向的。如果你只想突出显示正向连接的邻居,那么就消除OR的后半部分。)

一种更有效的计算方法,如果你必须经常这样做,就是有一个地图或矩阵,它允许进行恒定时间查找来测试a和b是否是邻居。例如:

var linkedByIndex = {};
links.forEach(function(d) {
  linkedByIndex[d.source.index + "," + d.target.index] = 1;
});

现在你可以说:

function neighboring(a, b) {
  return linkedByIndex[a.index + "," + b.index];
}

因此,您现在可以迭代节点并正确更新其不透明度:

node.style("opacity", function(o) {
  return neighboring(d, o) ? 1 : opacity;
});

(您可能还想通过为linkedByIndex中的每个节点设置自我链接,或者在计算样式时直接测试d来特殊设置鼠标悬停链接本身,或者通过使用!important css :hover样式。)

我在代码中要改变的最后一件事是使用填充不透明度和笔触不透明度而不是不透明度,因为它们提供了更好的性能。