在D3力向图中突出显示选定的节点,其链接及其子级


70

我正在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);
        });
    };
}

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

我以淡化http://mbostock.github.com/d3/ex/chord.html中的链接为例:

在此处输入图片说明

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

任何有关如何解决或改善此问题的好建议都会被强烈建议:)

Answers:


93

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

您已经突出显示了该行,但是我可能会将您的代码合并为一个迭代,如下所示:

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

突出显示相邻节点比较困难,因为您需要了解每个节点的相邻节点。使用当前的数据结构很难确定此信息,因为您所拥有的全部都是节点数组和链接数组。忘记了第二的DOM,并问自己,你将如何确定是否两个节点a,并b为邻?

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样式。)

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


1
@mbostock效果很好,非常感谢:D我已经用您的解决方案更新了jsfiddle
Christopher Manning

迈克,这个解决方案简直就是美。只是在说'。
Vivek

迈克,有没有办法从数据对象到DOM元素?我编写了一种方法,可以将所有相邻数据节点都移到“鼠标悬停”节点上。如何更改属于这些数据节点的DOM元素上的CSS / Style / Class / ...?
fgysin恢复Monica 2012年

1
@fgysin下面是一些可能的方法:stackoverflow.com/questions/11206015/...
mbostock

7
正如kdazzle所说,该小提琴试图动态加载json数据,并且数据不再存在,并且jsfiddle也不想加载X域。这是一个内联数据的更新小提琴: jsfiddle.net/tristanreid/xReHA/636
Tristan Reid
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.