d3.js - 用线条连接形状(不使用力或其他布局)

时间:2014-02-02 13:34:41

标签: javascript d3.js

我有一堆静态圆圈,我想用线条连接它们(这是一个依赖图)。我看到的所有例子都是用d3的现成布局完成的,我不知道如何有效地处理这个问题。我还想在鼠标悬停在该节点上时突出显示与节点相关的线条,以及淡化任何其他形状/线条。

这就是我现在所拥有的:(它只是根据给定的区域大小绘制均匀间隔和大小的圆圈)

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
</head>
<body style="overflow: hidden;">
     <div id="drawarea"  style="overflow: hidden;"></div>

   <script type="text/javascript">


var dataset = [],
    i = 0;

for(i=0; i<45; i++){
    dataset.push(Math.round(Math.random()*100));
}    

 var   width = 5000,
       height = 3000;

var svg = d3.select("#drawarea").append("svg")
    .attr("width", width)
    .attr("height", height)
    .call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
  .append("g");

var div_area = width*height,
    num_nodes = dataset.length,
    node_area = div_area/num_nodes*0.7,
    node_to_padding_ratio = 0.50,
    node_dia_inc_pad = Math.sqrt(node_area),
    node_radius_wo_pad = node_dia_inc_pad/2*node_to_padding_ratio,
    node_padding = node_dia_inc_pad/2*(1-node_to_padding_ratio),
    nodes_in_width = parseInt(width/(node_dia_inc_pad)),
    nodes_in_height = parseInt(height/(node_dia_inc_pad));  

svg.selectAll("circle")
    .data(dataset)
    .enter().append("circle")
    .style("stroke", "gray")
    .style("fill", "white")
    .attr("r", node_radius_wo_pad)
    .attr("cx", function(d, i){ return 2*node_radius_wo_pad+i%nodes_in_width*node_dia_inc_pad;})
    .attr("cy", function(d, i){ return 2*node_radius_wo_pad+(parseInt(i/nodes_in_width))*node_dia_inc_pad})
    .on("mouseover", function(){d3.select(this).style("fill", "aliceblue");})
    .on("mouseout", function(){d3.select(this).style("fill", "white");})

function zoom() {
  svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}

    </script>     
</body>
</html>

编辑:我修改后的代码:

    <!DOCTYPE html>
    <html>
    <head>
        <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
    </head>
    <body style="overflow: hidden;">
         <div id="canvas"  style="overflow: hidden;"></div>

       <script type="text/javascript">
    var graph = {
      "nodes":[
        {"name":"Myriel","group":1},
        {"name":"Napoleon","group":1}
      ],
      "links":[
        {"source":1,"target":0,"value":1}
      ]
    }


      var   width = 2000,
       height = 1000;

var svg = d3.select("#canvas").append("svg")
    .attr("width", width)
    .attr("height", height)
    .call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
  .append("g");



var div_area = width*height,
    num_nodes = graph.nodes.length,
    node_area = div_area/num_nodes,
    node_to_padding_ratio = 0.50,
    node_dia_inc_pad = Math.sqrt(node_area),
    node_radius_wo_pad = node_dia_inc_pad/2*node_to_padding_ratio,
    node_padding = node_dia_inc_pad/2*(1-node_to_padding_ratio),
    nodes_in_width = parseInt(width/(node_dia_inc_pad)),
    nodes_in_height = parseInt(height/(node_dia_inc_pad));  


var xScale = d3.scale.linear()
    .domain([0,nodes_in_width])
    .range([node_radius_wo_pad,width-node_radius_wo_pad]);

var yScale = d3.scale.linear()
    .domain([0,nodes_in_height])
    .range([node_radius_wo_pad,height-node_radius_wo_pad]);


var lines = svg.attr("class", "line")
  .selectAll("line").data(graph.links)
  .enter().append("line")
  .attr("x1", function(d) { return xScale(d.source%nodes_in_width); })
  .attr("y1", function(d) { return yScale(parseInt(d.source/nodes_in_width)); })
  .attr("x2", function(d) { return xScale(d.target%nodes_in_width); })
  .attr("y2", function(d) {  return yScale(parseInt(d.target/nodes_in_width)); })
  .attr("src", function(d) {  return d.source; })
  .attr("trgt", function(d) {  return d.target; })
  .style("stroke", "grey");

var circles = svg.selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .style("stroke", "gray")
    .style("fill", "white")
    .attr("r", node_radius_wo_pad)
    .attr("cx", function(d, i){ return xScale(i%nodes_in_width);})
    .attr("cy", function(d, i){ return yScale(parseInt(i/nodes_in_width));})
    .attr("index", function(d, i){return i;})
    .on("mouseover", function(){
        var that = this;
        lines.filter(function() {   
            return d3.select(this).attr("src") == d3.select(that).attr("index");
           }).style("stroke", "red");
        lines.filter(function() {
            return d3.select(this).attr("trgt") == d3.select(that).attr("index");
           }).style("stroke", "green");
        lines.filter(function() {
            return (d3.select(this).attr("trgt") != d3.select(that).attr("index") && d3.select(this).attr("src") != d3.select(that).attr("index"));
           }).style("display", "none");
        d3.select(this).style("fill", "aliceblue");
     })
    .on("mouseout", function(){
      lines.style("stroke", "grey")
           .style("display", "block");
      d3.select(this).style("fill", "white");
    });






function zoom() {
      svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
    }
    </script>     
</body>
</html>

我现在要做的是让线条指向的圆圈和类似的颜色。我不知道如何从圆圈的“鼠标悬停”事件中引用它们。会做一些测试......

1 个答案:

答案 0 :(得分:5)

您尚未指定节点的连接方式,因此我假设所有内容都与所有内容相关联。原则与任何其他布局相同 - 您获取确定链接的数据并将其传递给.data()。在你的代码中,坐标不是数据的一部分,这使得它更冗长,但仍然非常简单。

要添加链接,我正在使用nested selection - 我为每个节点添加了g元素,并在与所有其他节点的连接下面。

var lines = svg.selectAll("g.line").data(dataset)
  .enter().append("g").attr("class", "line")
  .selectAll("line").data(dataset)
  .enter().append("line")
  .attr("x1", function(d, i) { return 2*node_radius_wo_pad+i%nodes_in_width*node_dia_inc_pad; })
  .attr("y1", function(d, i) { return 2*node_radius_wo_pad+(parseInt(i/nodes_in_width))*node_dia_inc_pad; })
  .attr("x2", function(d, i, j) { return 2*node_radius_wo_pad+j%nodes_in_width*node_dia_inc_pad; })
  .attr("y2", function(d, i, j) { return 2*node_radius_wo_pad+(parseInt(j/nodes_in_width))*node_dia_inc_pad; });

这为每对节点添加一行。请注意,它将在相同节点(您将无法看到)之间添加链接,并在每对节点之间添加2个链接 - 一次从一个节点开始,一次在另一个节点开始。我没有在这里过滤掉这些案例以保持代码简单。在您的特定应用程序中,我猜测无论如何都以另一种方式确定连接。

要突出显示在突出显示时连接特定节点的链接,我使用包含所有节点的links变量,并过滤掉其起始坐标与圆坐标不同的变量。然后将过滤后的选择涂成红色。

.on("mouseover", function(){
   var that = this;
   lines.filter(function() {
       return d3.select(this).attr("x1") == d3.select(that).attr("cx") && d3.select(this).attr("y1") == d3.select(that).attr("cy");
     }).style("stroke", "red");
   d3.select(this).style("fill", "aliceblue");
})

如果坐标是数据的一部分,那么一切都会变得容易一些,看起来更像是你在力布局中看到的例子。我建议创建一个与您的链接一样的数据结构,使用sourcetarget属性来确定源节点和目标节点。

完整示例here