当前位置:   article > 正文

D3.js 第14课 力导向图_d3.js力导向图

d3.js力导向图

力导向图: 每一个节点都受到力的作用而运动,这种是一种非常绚丽的图表。
这里写图片描述

力导向图(Force-Directed Graph),是绘图的一种算法。

在二维或三维空间里配置节点,节点之间用线连接,称为连线。

各连线的长度几乎相等,且尽可能不相交。节点和连线都被施加了力的作用,力是根据节点和连线的相对位置计算的。

根据力的作用,来计算节点和连线的运动轨迹,并不断降低它们的能量,最终达到一种能量很低的安定状态。

力导向图能表示节点之间的多对多的关系

数据

初始数据如下:

var nodes = [ { name: "桂林" }, { name: "广州" },
              { name: "厦门" }, { name: "杭州" },
              { name: "上海" }, { name: "青岛" },
              { name: "天津" } ];

 var edges = [ { source : 0 , target: 1 } , { source : 0 , target: 2 } ,
               { source : 0 , target: 3 } , { source : 1 , target: 4 } ,
               { source : 1 , target: 5 } , { source : 1 , target: 6 } ];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

节点(nodes)和连线(edges)的数组
节点是一些城市名
连线的两端是节点的序号(序号从 0 开始)。

布局(数据转换)

定义一个力导向图的布局如下。

var force = d3.layout.force()
      .nodes(nodes) //指定节点数组
      .links(edges) //指定连线数组
      .size([width,height]) //指定作用域范围
      .linkDistance(150) //指定连线长度
      .charge([-400]); //相互之间的作用力
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后,使力学作用生效

force.start();    //开始作用
  • 1

如此,数组 nodes 和 edges 的数据都发生了变化。

在控制台输出一下,看看发生了什么变化。

console.log(nodes);
console.log(edges);
  • 1
  • 2

节点转换前后如下图`
这里写图片描述
节点对象里多了一些变量:

  • index - 节点的索引号
  • px, py - 节点上一个时刻的坐标
  • x, y - 节点的当前坐标
  • weight - 节点的权重

看连线的变化
这里写图片描述
可以看到,连线的两个节点序号,分别变成了对应的节点对象

绘制

有了转换后的数据,就可以作图了。分别绘制三种图形元素

  • line - 线段,表示连线。
  • circle - 圆,表示节点。
  • text - 文字,描述节点。
//添加连线 
 var svg_edges = svg.selectAll("line")
     .data(edges)
     .enter()
     .append("line")
     .style("stroke","#ccc")
     .style("stroke-width",1);

 var color = d3.scale.category20();

 //添加节点 
 var svg_nodes = svg.selectAll("circle")
     .data(nodes)
     .enter()
     .append("circle")
     .attr("r",20)
     .style("fill",function(d,i){
         return color(i);
     })
     .call(force.drag);  //使得节点能够拖动

 //添加描述节点的文字
 var svg_texts = svg.selectAll("text")
     .data(nodes)
     .enter()
     .append("text")
     .style("fill", "black")
     .attr("dx", 20)
     .attr("dy", 8)
     .text(function(d){
        return d.name;
     });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

调用 call( force.drag ) 后节点可被拖动。force.drag() 是一个函数,将其作为 call() 的参数,相当于将当前选择的元素传到 force.drag() 函数中。

最后,还有一段最重要的代码。由于力导向图是不断运动的,每一时刻都在发生更新,因此,必须不断更新节点和连线的位置。

力导向图布局 force 有一个事件 tick,每进行到一个时刻,都要调用它,更新的内容就写在它的监听器里就好。

force.on("tick", function(){ //对于每一个时间间隔
    //更新连线坐标
    svg_edges.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; });

    //更新节点坐标
    svg_nodes.attr("cx",function(d){ return d.x; })
        .attr("cy",function(d){ return d.y; });

    //更新文字坐标
    svg_texts.attr("x", function(d){ return d.x; })
       .attr("y", function(d){ return d.y; });
 });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

tick 的英文意思是钟表发出的嘀嗒嘀嗒声,想到这个大家应该很清楚了吧。每次触发时,都会调用后面的无名函数 function。

最终效果图:
这里写图片描述

源码如下:

<html>  
  <head>  
        <meta charset="utf-8">  
        <title>力导向图</title>  
  </head> 

<style>


</style>
    <body>  
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>  
        <script>           

        var nodes = [ { name: "桂林"    }, { name: "广州" },
                      { name: "厦门"    }, { name: "杭州"   },
                      { name: "上海"   }, { name: "青岛"    },
                      { name: "天津"    } ];

        var edges = [  { source : 0  , target: 1 } , { source : 0  , target: 2 } ,
                       { source : 0  , target: 3 } , { source : 1  , target: 4 } ,
                       { source : 1  , target: 5 } , { source : 1  , target: 6 }  ];    

        var width = 400;
        var height = 400;


        var svg = d3.select("body")
                    .append("svg")
                    .attr("width",width)
                    .attr("height",height);

        var force = d3.layout.force()
                .nodes(nodes)       //指定节点数组
                .links(edges)       //指定连线数组
                .size([width,height])   //指定范围
                .linkDistance(150)  //指定连线长度
                .charge(-400);  //相互之间的作用力

        force.start();  //开始作用

        console.log(nodes);
        console.log(edges);

        //添加连线      
        var svg_edges = svg.selectAll("line")
                            .data(edges)
                            .enter()
                            .append("line")
                            .style("stroke","#ccc")
                            .style("stroke-width",1);

        var color = d3.scale.category20();

        //添加节点          
        var svg_nodes = svg.selectAll("circle")
                            .data(nodes)
                            .enter()
                            .append("circle")
                            .attr("r",20)
                            .style("fill",function(d,i){
                                return color(i);
                            })
                            .call(force.drag);  //使得节点能够拖动

        //添加描述节点的文字
        var svg_texts = svg.selectAll("text")
                            .data(nodes)
                            .enter()
                            .append("text")
                            .style("fill", "black")
                            .attr("dx", 20)
                            .attr("dy", 8)
                            .text(function(d){
                                return d.name;
                            });


        force.on("tick", function(){    //对于每一个时间间隔

             //更新连线坐标
             svg_edges.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; });

             //更新节点坐标
             svg_nodes.attr("cx",function(d){ return d.x; })
                    .attr("cy",function(d){ return d.y; });

             //更新文字坐标
             svg_texts.attr("x", function(d){ return d.x; })
                .attr("y", function(d){ return d.y; });
        });

        </script>  

    </body>  
</html>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/132715?site=
推荐阅读
相关标签
  

闽ICP备14008679号