【问题标题】:How to implement highlight and transition effect for nodes in D3js force directed graph?如何实现 D3js 强制有向图中节点的高亮和过渡效果?
【发布时间】:2020-03-19 17:47:41
【问题描述】:

我正在尝试在 D3 js 强制有向图中的节点上实现高光效果。

这样做时我面临以下问题。

高光效果: 一种。在选定节点的鼠标悬停时,我正在更改其相邻节点的颜色。但是其他节点的颜色不应该改变(在我的情况下,它变成红色并且不知道如何修复它)如何解决这个问题? 湾。在选定节点的鼠标悬停时,我想通过增加它的半径来为所有互连节点添加过渡效果。 其他节点也应该淡出,这可能吗?

请参考工作 js fiddle 以供参考: enter link description here

var nodeElements =  g.append("g")
  .attr("class", "nodes")
  .selectAll("circle")
  .data(graph.nodes)
  .enter().append("circle")
  .attr("r", 40)
.attr("fill", function(d) { return color(d.id); })
  .attr("stroke", "#fff")
  .attr('stroke-width', 21)
  .attr("id", function(d) { return d.id })
      .on('mouseover', selectNode)
      .on('mouseout', releaseNode)
  .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

function releaseNode() {
   nodeElements.attr("fill", function(d) { return color(d.id)
   })
}

function selectNode(selectedNode) {
  var neighbors = getNeighbors(selectedNode)
  nodeElements.attr('fill', function(node) {
    return getNodeColor(node, neighbors)
  })
}

function getNeighbors(node) {
  return graph.links.reduce(function(neighbors, link) {
    if (link.target.id === node.id) {
      neighbors.push(link.source.id)
    } else if (link.source.id === node.id) {
      neighbors.push(link.target.id)
    }
    return neighbors
  }, [node.id])
}

function getNodeColor(node, neighbors) {
  // If is neighbor
  if (Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1) {
    return 'rgba(251, 130, 30, 1)'
    // return node.level === 1 ? '#9C4A9C' : 'rgba(251, 130, 30, 1)'
  } else {
    return 'red';
  }
  //return node.level === 0 ? '#91007B' : '#D8ABD8'
}

【问题讨论】:

    标签: javascript d3.js d3-force-directed


    【解决方案1】:
    1. 要在鼠标悬停时不改变其他节点的颜色,您必须更改下面的getNodeColor 函数:

      function getNodeColor(node, neighbors) {
        // If is neighbor
        if ( neighbors.indexOf(node.id) > -1) {
          return 'rgba(251, 130, 30, 1)'
        } 
        else {
              return color(node.id);
        }
      }
      
    2. 为了扩大节点,您必须在 selectNode 函数中调用另一个函数 getNodeRadius,该函数被称为 on('mouseover', selectNode);

      function selectNode(selectedNode) {
        var neighbors = getNeighbors(selectedNode)
        nodeElements.transition()
              .duration(500)
              .attr('fill', function(node) {
                  return getNodeColor(node, neighbors)
                })
              .attr('r', function(node) {
                return getNodeRadius(node,neighbors);
              })
      }
      

    注意.transition().duration(500) 的方法表示以 500 毫秒的持续时间动画到此结束状态。

    getNodeRadius函数需要定义为:

    function getNodeRadius(node, neighbors) {
      // If is neighbor
      if ( neighbors.indexOf(node.id) > -1) {
        return '60'
      } 
      else {
            return '40'
      }
    }
    

    下面的完整工作示例:

    var graph = {
    'nodes':[
    {'id':'Material_Definition','group':0},
    {'id':'Lot1','group':1},
    {'id':'Lot2','group':1},
    {'id':'Lot3','group':1},
    {'id':'Lot4','group':1},
    {'id':'Lot5','group':1},
    {'id':'Lot6','group':1},
    {'id':'Lot7','group':1},
    {'id':'Lot8','group':1},
    {'id':'Lot9','group':1},
    {'id':'Lot10','group':1},
    {'id':'Lot11','group':1},
    {'id':'Lot12','group':1},
    {'id':'Lot13','group':1},
    {'id':'Lot14','group':1},
    {'id':'Lot15','group':1},
    {'id':'Lot16','group':1},
    {'id':'Lot17','group':1},
    {'id':'Lot18','group':1},
    {'id':'Lot19','group':1},
    {'id':'Lot20','group':1},
    {'id':'SubLot1_Lot1','group':2},
    {'id':'SubLot2_Lot1','group':2},
    {'id':'SubLot3_Lot1','group':2},
    {'id':'SubLot4_Lot1','group':2},
    {'id':'SubLot5_Lot1','group':2},
    {'id':'SubLot6_Lot1','group':2},
    {'id':'SubLot1_Lot2','group':2},
    {'id':'SubLot2_Lot2','group':2},
    {'id':'SubLot3_Lot2','group':2},
    {'id':'SubLot4_Lot2','group':2},
    {'id':'SubLot5_Lot2','group':2},
    {'id':'SubLot6_Lot2','group':2}],
    'links':[
    /* Material Definition linked to Lots */
    {'source':'Material_Definition','target':'Lot1','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot2','value':8,'type':'A'},
    {'source':'Material_Definition','target':'Lot3','value':10,'type':'A'},
    {'source':'Material_Definition','target':'Lot3','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot4','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot5','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot6','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot7','value':2,'type':'A'},
    {'source':'Material_Definition','target':'Lot8','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot9','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot10','value':8,'type':'A'},
    {'source':'Material_Definition','target':'Lot11','value':10,'type':'A'},
    {'source':'Material_Definition','target':'Lot12','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot13','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot14','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot15','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot16','value':2,'type':'A'},
    {'source':'Material_Definition','target':'Lot17','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot18','value':1,'type':'A'},
    {'source':'Material_Definition','target':'Lot19','value':2,'type':'A'},
    {'source':'Material_Definition','target':'Lot20','value':1,'type':'A'},
    
    /* Lot1 is linked to Sublots */
    {'source':'Lot1','target':'SubLot1_Lot1','value':2,'type':'A'},
    {'source':'Lot1','target':'SubLot2_Lot1','value':1,'type':'A'},
    {'source':'Lot1','target':'SubLot3_Lot1','value':2,'type':'A'},
    {'source':'Lot1','target':'SubLot4_Lot1','value':1,'type':'A'},
    {'source':'Lot1','target':'SubLot5_Lot1','value':2,'type':'A'},
    {'source':'Lot1','target':'SubLot6_Lot1','value':1,'type':'A'},
    
    /* Lot2 is linked to Sublots */
    {'source':'Lot2','target':'SubLot1_Lot2','value':2,'type':'A'},
    {'source':'Lot2','target':'SubLot2_Lot2','value':1,'type':'A'},
    {'source':'Lot2','target':'SubLot3_Lot2','value':2,'type':'A'},
    {'source':'Lot2','target':'SubLot4_Lot2','value':1,'type':'A'},
    {'source':'Lot2','target':'SubLot5_Lot2','value':2,'type':'A'},
    {'source':'Lot2','target':'SubLot6_Lot2','value':1,'type':'A'},
    
    /* Interconnected Lots */
    {'source':'Lot10','target':'Lot18','value':2,'type':'A'},
    {'source':'Lot10','target':'Lot19','value':1,'type':'A'},
    {'source':'Lot10','target':'Lot20','value':2,'type':'A'},
    {'source':'Lot7','target':'Lot8','value':1,'type':'A'},
    {'source':'Lot7','target':'Lot9','value':2,'type':'A'},
    {'source':'Lot7','target':'Lot10','value':1,'type':'A'},
    {'source':'Lot12','target':'Lot4','value':2,'type':'A'},
    {'source':'Lot12','target':'Lot3','value':1,'type':'A'},
    {'source':'Lot12','target':'Lot2','value':2,'type':'A'},
    {'source':'Lot16','target':'Lot1','value':1,'type':'A'},
    {'source':'Lot16','target':'Lot9','value':2,'type':'A'},
    {'source':'Lot16','target':'Lot12','value':1,'type':'A'}
    ]};
    
    
    var svg = d3.select("svg"),
      width = +svg.attr("width"),
      height = +svg.attr("height");
    
    var color = d3.scaleOrdinal(d3.schemeCategory10);
    var zoom_handler = d3.zoom().on("zoom", zoom_actions);
    
    // zoom_handler(svg);
    
    var simulation = d3.forceSimulation()
      .force("link", d3.forceLink().distance(300).id(function(d) {
        return d.id;
      }))
      .force("charge", d3.forceManyBody().strength(-300))
      .force("center", d3.forceCenter(width / 2, height / 2));
    
    var g = svg.append("g")
      .attr("class", "everything");
    
    svg.call(zoom_handler)
      .call(zoom_handler.transform, d3.zoomIdentity.translate(200, 150).scale(0.2));
    
    
    var linkElements = g.append("g")
      .attr("class", "links")
      .selectAll("line")
      .data(graph.links)
      .enter().append("line")
      .style("stroke-width",5.5)
      .style("stroke",'black');
    
    var nodeElements =  g.append("g")
      .attr("class", "nodes")
      .selectAll("circle")
      .data(graph.nodes)
      .enter().append("circle")
      .attr("r", 40)
      .attr('class', 'nodecircles')
    .attr("fill", function(d) { return color(d.id); })
      .attr("stroke", "#fff")
      .attr('stroke-width', 21)
      .attr("id", function(d) { return d.id })
          .on('mouseover', selectNode)
          .on('mouseout', releaseNode)
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));
        
    function releaseNode(d) {
       nodeElements.transition().duration(500)
       .attr("fill", function(d) { return color(d.id)
       })
       .attr('r', 40);
    }
      
    var textElements = g.append("g")    // use g.append instead of svg.append to enable zoom
      .attr("class", "texts")
      .selectAll("text")
      .data(graph.nodes)
      .enter().append("text")
      .text(function(node) {
        return node.id
      })
      .attr("font-size", 55)
      .attr("font-family", "sans-serif")
      .attr("text-anchor", "middle")
      .attr("fill", "black")
      .attr("style", "font-weight:bold; text-stroke: 1px #fff;")
      .attr("dx", 0)
      .attr("dy", 20)
    
    function ticked() {
      linkElements
        .attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });
      nodeElements
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; })
        .each(d => { d3.select('#t_' + d.id).attr('x', d.x + 10).attr('y', d.y + 3); });
        textElements
        .attr('x', function(d) {
          return d.x
        })
        .attr('y', function(d) {
          return d.y
        });
        
    }
    
    simulation
      .nodes(graph.nodes)
      .on("tick", ticked);
    
    simulation.force("link")
      .links(graph.links);
    
    
    function zoom_actions() {
      g.attr("transform", d3.event.transform)
    }
    
    function dragstarted(d) {
      if (!d3.event.active) simulation.alphaTarget(0.3).restart();
      d.fx = d.x;
      d.fy = d.y;
    }
    
    function dragged(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
    
    function dragended(d) {
      if (!d3.event.active) simulation.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }
    
    function selectNode(selectedNode) {
      var neighbors = getNeighbors(selectedNode)
      nodeElements.transition()
        .duration(500)
        .attr('fill', function(node) {
        return getNodeColor(node, neighbors)
      })
        .attr('r', function(node) {
        return getNodeRadius(node,neighbors);
      })
    }
    
    function getNeighbors(node) {
      return graph.links.reduce(function(neighbors, link) {
        if (link.target.id === node.id) {
          neighbors.push(link.source.id)
        } else if (link.source.id === node.id) {
          neighbors.push(link.target.id)
        }
        return neighbors
      }, [node.id])
    }
    
    function getNodeColor(node, neighbors) {
      // If is neighbor
      if ( neighbors.indexOf(node.id) > -1) {
        return 'rgba(251, 130, 30, 1)'
      } 
      else {
    		return color(node.id);
      }
    }
    
    function getNodeRadius(node, neighbors) {
      // If is neighbor
      if ( neighbors.indexOf(node.id) > -1) {
        return '60'
      } 
      else {
    		return '40'
      }
    }
       .links line {
      stroke: #999;
      stroke-opacity: 0.6;
    }
    
    .nodes circle {
      stroke: #000;
      stroke-width: 1.5px;
    }
    
    text {
      font-size: 10px;
    }
    <!DOCTYPE html>
    <meta charset="utf-8">
    <link rel="shortcut icon" href="//#" />
    
    <html>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    
    
    </html>
    
    <svg width="798" height="400"></svg>
    <script src="https://d3js.org/d3.v4.min.js"></script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-02
      • 2013-10-09
      • 2018-05-07
      • 2017-10-21
      • 2013-01-14
      • 1970-01-01
      • 2020-09-01
      • 1970-01-01
      相关资源
      最近更新 更多