将几个鼠标悬停事件应用于相邻(连接)节点

时间:2013-05-31 12:56:49

标签: d3.js mouseover highlight force-layout

我有一个网络图(强制导向图),一个散点图和一个全部互连的表(参见jsFiddle)。我有互连工作方式我希望它们用于鼠标悬停事件。我想修改我的代码,以便当我将鼠标悬停在网络图中的节点上时,不仅突出显示了moused-over节点(以及它在散点图和表中的连接),而且还突出显示了它的直接邻居节点(以及作为他们在散点图和表格中的连接。)

我查看了Highlight selected node, its links, and its children in a D3 force directed graph中的信息以寻求帮助。在某个地方(不完全确定在哪里),我找到了一个帮助定义连接节点的函数示例isConnected()

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

我想将此功能合并到我的鼠标悬停事件中,可能使用if()语句,这样我就可以完成我想要的所有“突出显示”。但是,我是D3和js的新手,我不知道如何设置它。

以下是我想要修改的代码片段(来自jsFiddle)。我将不胜感激任何有关其他例子的建议或指示。

var node = svg.selectAll(".node")
    .data(graph.nodes)
    .enter().append("g")
    .attr("class", function(d) { return "node " + d.name + " " + d.location; })
    .call(force.drag)
    .on("mouseover", function(d) { 
        // I would like to insert an if statement to do all of these things to the connected nodes
        // if(isConnected(d, o)) {
            d3.select(this).select("circle").style("stroke-width", 6); 
            d3.select(this).select("circle").style("stroke", "orange"); 
            d3.select(this).select("text").style("font", "20px sans-serif");
            d3.selectAll("rect." + d.location).style("stroke-width", 6);
            d3.selectAll("rect." + d.location).style("stroke", "orange");
            d3.selectAll("text." + d.location).style("font", "20px sans-serif");
            d3.selectAll("tr." + d.name).style("background-color", "orange");
            //}
        })
    .on("mouseout",  function(d) { 
        // if(isConnected(d, o)) {
            d3.select(this).select("circle").style("stroke-width", 1.5); 
            d3.select(this).select("circle").style("stroke", "gray"); 
            d3.select(this).select("text").style("font", "12px sans-serif");
            d3.selectAll("rect." + d.location).style("stroke-width", 1.5);
            d3.selectAll("rect." + d.location).style("stroke", "gray");
            d3.selectAll("text." + d.location).style("font", "12px sans-serif");
            d3.selectAll("tr." + d.name).style("background-color", "white");
            //}
        });

2 个答案:

答案 0 :(得分:5)

在另一种情况下,我会将我的可视对象放入图形数据结构中并导航以有效地更新相应的项目。但这是d3,但我们将做同样的事情,但我们将使用d3选择(可以像图形,但为此它们看起来更像数组)而不是我们创建的图形数据结构。从算法上讲,这种方法效率不高,但我们的图表很小。

所以向后工作我会想要一个只包括被挑选节点的邻居的选择 节点。我将通过选择所有圆圈然后使用d3选择过滤器方法将其减少到只有邻居的圆圈来完成此操作。

当然,我需要邻居列表,但是一些不错的js数组方法可以做到这一点。最后的相关代码(鼠标悬停)甚至没有那么久 - 但我添加了一堆评论:

// Figure out the neighboring node id's with brute strength because the graph is small
var nodeNeighbors = graph.links.filter(function(link) {
    // Filter the list of links to only those links that have our target 
    // node as a source or target
    return link.source.index === d.index || link.target.index === d.index;})
.map(function(link) {
    // Map the list of links to a simple array of the neighboring indices - this is
    // technically not required but makes the code below simpler because we can use         
    // indexOf instead of iterating and searching ourselves.
    return link.source.index === d.index ? link.target.index : link.source.index; });

// Reset all circles - we will do this in mouseout also
svg.selectAll('circle').style('stroke', 'gray');

// now we select the neighboring circles and apply whatever style we want. 
// Note that we could also filter a selection of links in this way if we want to 
// Highlight those as well
svg.selectAll('circle').filter(function(node) {
    // I filter the selection of all circles to only those that hold a node with an
    // index in my listg of neighbors
    return nodeNeighbors.indexOf(node.index) > -1;
})
.style('stroke', 'orange');

您也可以尝试fiddle

我认为这里重要的d3概念是当你将数据与一个元素相关联时(通常在选择中使用data()或datum()方法)那么这个数据就会粘在那个元素上,以后任何选择都会一直使用它

要链接其他方面,您可以以类似的方式提取这些属性,并通过d3链接它们。例如,您可以添加到鼠标悬停的位置矩形:

var nodeLocations = graph.links.filter(function(link) {
        return link.source.index === d.index || link.target.index === d.index;})
    .map(function(link) {
        return link.source.index === d.index ? link.target.location : link.source.location; });

d3.selectAll("rect").filter(function(node) { return nodeLocations.indexOf(node.location) > -1; }) .style("stroke", "cyan");

答案 1 :(得分:0)

我建立的这个东西是通过自我网络功能实现的:

https://gist.github.com/emeeks/4588962

将.on(“mouseover”,findEgo)添加到您的节点,只要您具有某种标识uid属性,就可以使用以下内容:如果您不方便加载节点,则可以生成这些属性。这有点过分,因为它允许n度自我网络,并为其他网络分析功能创建聚合表,但基本功能将为您提供您想要的,您或其他用户可能会发现该方面有用:

 function findEgo(d) {  
  var computedEgoArray = findEgoNetwork(d.id, 1, false,"individual");
  d3.selectAll("circle.node").style("fill", function(p) {return p.id == d.id ? "purple" : computedEgoArray.indexOf(p.id) > -1 ? "blue" : "pink"})
 }

 function findEgoNetwork(searchNode, egoNetworkDegree, isDirected, searchType) {
  var egoNetwork = {};
  for (x in nodes) {
  if (nodes[x].id == searchNode || searchType == "aggregate") {
   egoNetwork[nodes[x].id] = [nodes[x].id];
   var z = 0;
   while (z < egoNetworkDegree) {
    var thisEgoRing = egoNetwork[nodes[x].id].slice(0);
    for (y in links) {
     if (thisEgoRing.indexOf(links[y].source.id) > -1 && thisEgoRing.indexOf(links[y].target.id) == -1) {
     egoNetwork[nodes[x].id].push(links[y].target.id)
     }
    else if (isDirected == false && thisEgoRing.indexOf(links[y].source.id) == -1 && thisEgoRing.indexOf(links[y].target.id) > -1) {
    egoNetwork[nodes[x].id].push(links[y].source.id)
    }
 }
 z++;
 }
 }
 }
 if (searchType == "aggregate") {
 //if it's checking the entire network, pass back the entire object of arrays
 return egoNetwork;
 }
 else {
 //Otherwise only give back the array that corresponds with the search node
 return egoNetwork[searchNode];
 }
 }