D3.js 力導向圖的制作


力導向圖中每一個節點都受到力的作用而運動,這種是一種非常絢麗的圖表。

force

力導向圖(Force-Directed Graph),是繪圖的一種算法。在二維或三維空間里配置節點,節點之間用線連接,稱為連線。

各連線的長度幾乎相等,且盡可能不相交。節點和連線都被施加了力的作用,力是根據節點和連線的相對位置計算的。

根據力的作用,來計算節點和連線的運動軌跡,並不斷降低它們的能量,最終達到一種能量很低的安定狀態。

力導向圖能表示節點之間的多對多的關系

 

 

 

1. 數據

  初始數據如下:

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 } ];

  節點(nodes)和連線(edges)的數組,節點是一些城市名,連線的兩端是節點的序號(序號從 0 開始)。

  這些數據是不能作圖的,因為不知道節點和連線的坐標。這句話一說出來,就請想到布局。這里用到的布局是:d3.layout.force()

 

 

 

2. 布局(數據轉換)

  定義一個力導向圖的布局如下。

var force = d3.layout.force()
      .nodes(nodes) //指定節點數組
      .links(edges) //指定連線數組
      .size([width,height]) //指定作用域范圍
      .linkDistance(150) //指定連線長度
      .charge([-400]); //相互之間的作用力

 

  然后,使力學作用生效:

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

  如此,數組 nodes 和 edges 的數據都發生了變化。在控制台輸出一下,看看發生了什么變化。

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

  節點轉換前后如下圖。

  921

  轉換后,節點對象里多了一些變量。其意義如下:

    • index:節點的索引號
    • px, py:節點上一個時刻的坐標
    • x, y:節點的當前坐標
    • weight:節點的權重

  再來看看連線的變化。

  923

  可以看到,連線的兩個節點序號,分別變成了對應的節點對象。

 

 

 

 

 

3. 繪制

  有了轉換后的數據,就可以作圖了。分別繪制三種圖形元素:

    • 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;
     });

  調用 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; });
 });

  tick 的英文意思是鍾表發出的嘀嗒嘀嗒聲,想到這個大家應該很清楚了吧。每次觸發時,都會調用后面的無名函數 function。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM