鍍金池/ 教程/ HTML/ 第十四章 力導(dǎo)向圖
第六章 比例尺的使用
第十一章 交互式操作
第十四章 力導(dǎo)向圖
第十三章 餅狀圖的制作
作者簡(jiǎn)介
第十二章 布局
第八章 完整的柱形圖
第十章 理解 Update、Enter、Exit
第十五章 樹(shù)狀圖
第三章 選擇元素和綁定數(shù)據(jù)
第七章 坐標(biāo)軸
第九章 讓圖表動(dòng)起來(lái)
第十六章 中國(guó)地圖
第四章 選擇、插入、刪除元素
第五章 做一個(gè)簡(jiǎn)單的圖表
第二章 第一個(gè)程序 HelloWorld
第一章 簡(jiǎn)介和安裝

第十四章 力導(dǎo)向圖

力導(dǎo)向圖(Force-Directed Graph),是繪圖的一種算法。在二維或三維空間里配置節(jié)點(diǎn),節(jié)點(diǎn)之間用線連接,稱為連線。各連線的長(zhǎng)度幾乎相等,且盡可能不相交。節(jié)點(diǎn)和連線都被施加了力的作用,力是根據(jù)節(jié)點(diǎn)和連線的相對(duì)位置計(jì)算的。根據(jù)力的作用,來(lái)計(jì)算節(jié)點(diǎn)和連線的運(yùn)動(dòng)軌跡,并不斷降低它們的能量,最終達(dá)到一種能量很低的安定狀態(tài)。

http://wiki.jikexueyuan.com/project/d3wiki/images/force-1.png" alt="力導(dǎo)向圖" />

力導(dǎo)向圖能表示節(jié)點(diǎn)之間的多對(duì)多的關(guān)系。

數(shù)據(jù)

初始數(shù)據(jù)如下:

var nodes = [ { name: "桂林" }, { name: "廣州" },
              { name: "廈門(mén)" }, { 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 } ];

節(jié)點(diǎn)(nodes)和連線(edges)的數(shù)組,節(jié)點(diǎn)是一些城市名,連線的兩端是節(jié)點(diǎn)的序號(hào)(序號(hào)從 0 開(kāi)始)。

這些數(shù)據(jù)是不能作圖的,因?yàn)椴恢拦?jié)點(diǎn)和連線的坐標(biāo)。這句話一說(shuō)出來(lái),就請(qǐng)想到布局。本章用到的布局是:d3.layout.force()

布局(數(shù)據(jù)轉(zhuǎn)換)

定義一個(gè)力導(dǎo)向圖的布局如下。

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

然后,使力學(xué)作用生效:

force.start();    //開(kāi)始作用

如此,數(shù)組 nodes 和 edges 的數(shù)據(jù)都發(fā)生了變化。在控制臺(tái)輸出一下,看看發(fā)生了什么變化。

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

節(jié)點(diǎn)轉(zhuǎn)換前后如下圖。

http://wiki.jikexueyuan.com/project/d3wiki/images/force-2.png" alt="節(jié)點(diǎn)轉(zhuǎn)換前后" />

轉(zhuǎn)換后,節(jié)點(diǎn)對(duì)象里多了一些變量。其意義如下:

  • index:節(jié)點(diǎn)的索引號(hào)
  • px, py:節(jié)點(diǎn)上一個(gè)時(shí)刻的坐標(biāo)
  • x, y:節(jié)點(diǎn)的當(dāng)前坐標(biāo)
  • weight:節(jié)點(diǎn)的權(quán)重

再來(lái)看看連線的變化。

http://wiki.jikexueyuan.com/project/d3wiki/images/force-3.png" alt="連線轉(zhuǎn)換前后" />

可以看到,連線的兩個(gè)節(jié)點(diǎn)序號(hào),分別變成了對(duì)應(yīng)的節(jié)點(diǎn)對(duì)象。

繪制

有了轉(zhuǎn)換后的數(shù)據(jù),就可以作圖了。分別繪制三種圖形元素:

  • line,線段,表示連線。
  • circle,圓,表示節(jié)點(diǎn)。
  • text,文字,描述節(jié)點(diǎn)。

代碼如下:

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

 var color = d3.scale.category20();

 //添加節(jié)點(diǎn) 
 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);  //使得節(jié)點(diǎn)能夠拖動(dòng)

 //添加描述節(jié)點(diǎn)的文字
 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;
     });

調(diào)用 call( force.drag ) 后節(jié)點(diǎn)可被拖動(dòng)。force.drag() 是一個(gè)函數(shù),將其作為 call() 的參數(shù),相當(dāng)于將當(dāng)前選擇的元素傳到 force.drag() 函數(shù)中。

最后,還有一段最重要的代碼。由于力導(dǎo)向圖是不斷運(yùn)動(dòng)的,每一時(shí)刻都在發(fā)生更新,因此,必須不斷更新節(jié)點(diǎn)和連線的位置。

力導(dǎo)向圖布局 force 有一個(gè)事件 tick,每進(jìn)行到一個(gè)時(shí)刻,都要調(diào)用它,更新的內(nèi)容就寫(xiě)在它的監(jiān)聽(tīng)器里就好。

force.on("tick", function(){ //對(duì)于每一個(gè)時(shí)間間隔
    //更新連線坐標(biāo)
    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; });

    //更新節(jié)點(diǎn)坐標(biāo)
    svg_nodes.attr("cx",function(d){ return d.x; })
        .attr("cy",function(d){ return d.y; });

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

tick 的英文意思是鐘表發(fā)出的嘀嗒嘀嗒聲,想到這個(gè)大家應(yīng)該很清楚了吧。每次觸發(fā)時(shí),都會(huì)調(diào)用后面的無(wú)名函數(shù) function。

結(jié)果如圖:

http://wiki.jikexueyuan.com/project/d3wiki/images/force-4.png" alt="結(jié)果" />

源代碼

下載地址:rm92.zip

展示地址:http://www.ourd3js.com/demo/rm/R-9.2/force.html