在 D3 力有向图中突出显示选定节点、其链接及其子节点

IT技术 javascript d3.js highlight force-layout chord
2021-01-14 21:43:15

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

在这个例子中,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淡化链接为例

在此处输入图片说明

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

关于如何解决或改进此问题的任何好的建议都将得到热烈的支持:)

1个回答

错误是因为您正在选择数据对象(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样式。)

我要在您的代码中更改的最后一件事是使用 fill-opacity 和 stroke-opacity 而不是 opacity,因为它们提供了更好的性能。

Mike,有没有办法从数据对象获取到 DOM 元素?我已经编写了一种方法来让我将所有相邻的数据节点连接到一个“鼠标悬停”节点。如何在属于这些数据节点的 DOM 元素上更改 CSS/Style/Class/...?
2021-03-22 21:43:15
@fgysin 以下是一些可能的方法:stackoverflow.com/questions/11206015/...
2021-03-31 21:43:15
效果很好@mbostock,非常感谢:D 我已经用你的解决方案更新了 jsfiddle
2021-04-04 21:43:15
正如 kdazzle 上面所说,那个 fiddle 试图动态加载 json 数据,并且数据不再存在,而且 jsfiddle 也不想加载 X 域。这是内联数据的更新小提琴: jsfiddle.net/tristanreid/xReHA/636
2021-04-07 21:43:15
迈克,这个解决方案简直太美了。只是在说'。
2021-04-09 21:43:15