D3力導向圖及樹狀佈局變換

D3力導向圖及樹狀佈局變換

d3的力導向圖是表現關係型數據比較方便且直觀的方法,可是會遇到節點比較多且層級關係混亂的狀況,這時樹狀佈局就比較方便了,如何不破壞原來結構以最小的代價變換樹狀佈局呢?下面將爲你們介紹。javascript

最終效果

http://en.jsrun.net/aXiKp/emb...java

代碼解析

首先咱們須要準備關係型數據,數據包括節點數據nodes和連線關係數據links,links的source和target分別表示源和目標的index。node

var nodes = [
  {value:"66666666",type:"home",index:"0"},
  {value:"11111111111",type:"phone",index:"1"},
  {value:"22222222222",type:"phone",index:"2"},
  {value:"33333333333",type:"phone",index:"3"},
  {value:"44444444444",type:"phone",index:"4"},
  {value:"55555555555",type:"phone",index:"5"},
  {value:"aaa",type:"weixin",index:"6"},
  {value:"bbb",type:"weixin",index:"7"},
  {value:"ccc",type:"weixin",index:"8"},
  {value:"ddd",type:"weixin",index:"9"},
  {value:"eee",type:"weixin",index:"10"},
  {value:"fff",type:"weixin",index:"11"},
];
var links = [
  {source:0,target:1},
  {source:0,target:2},
  {source:0,target:3},
  {source:0,target:4},
  {source:0,target:5},
  {source:2,target:6},
  {source:2,target:7},
  {source:2,target:8},
  {source:3,target:9},
  {source:3,target:10},
  {source:3,target:11},
]

繪製力導向圖

//新建畫布
var svg = d3.select("#forceMap").append("svg")
                        .attr("width",width)
            .attr("height",height)
            .attr("id","forceSvg");
//建立group,svg的繪製中爲了不混亂及後續事件的添加,建議使用g標籤將畫布分組。            
var mapG = svg.append("g")
.attr("id","forceGroup");
//使用d3的力學佈局,經過設定的屬性,將數據計算
var force = d3.layout.force()
                    .nodes(nodes)
                    .links(links)
                    .size([width,height])
                    .linkDistance(100)
                    .charge([-1250])
                    .gravity(0.5)
                    .friction(0.5);
force.start();//開始計算
//繪製線,svg的覆蓋順序是後面標籤覆蓋前面的,因此爲了不線在點上面,要先畫line
var linkG = mapG.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class","link")
.attr("stroke","#ccc");
//繪製點
var nodeG = mapG.selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class","node")
.attr("r",8)
.attr("fill",function(d){
  switch(d.type){
    case "home": return "red";break;
    case"phone": return "blue";break;
    case "weixin": return "green";break;
  }
});

//tick是力導向圖每一次運動須要計算的過程
force.on("tick", function () {
                      linkG.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;
                    });


                nodeG.attr("cx", function (d) {
                    return d.x
                })
                .attr("cy", function(d){
                  return d.y
                });
});

在tick中,d3自帶的佈局計算幫咱們完成了點和線每一次運動的座標,
咱們只須要在tick中從新賦給頁面中的點和線便可完成運動效果。數組

繪製樹狀佈局

在這裏,咱們利用d3的tree layout完成點的座標計算。
前提是必須把關係型數據改變爲樹狀層級數據。
用concat拷貝數組,避免影響全局數據。app

function drawTree(){
  var middleData = {};
  var linksBak = links.concat();
  var nodesBak = nodes.concat();
  //將數據整理爲樹狀結構
  nodesBak.forEach(function(d){
            if(d.index == 0){
                var temp = {
                    name:d.index,
                    children:[]
                };
                var treeData = toTreeData(linksBak);
                function toTreeData(data){
                    var pos={};
                    var tree=[];
                    var i=0;
                    while(data.length!=0){
                        if(data[i].source.index==d.index){
                            tree.push({
                                name:data[i].target.index,
                                children:[]
                            });
                            pos[data[i].target.index]=[tree.length-1];
                            data.splice(i,1);
                            i--;
                        }else{
                            var posArr=pos[data[i].source.index];
                            if(posArr!=undefined){

                                var obj=tree[posArr[0]];
                                for(var j=1;j<posArr.length;j++){
                                    obj=obj.children[posArr[j]];
                                }

                                obj.children.push({
                                    name:data[i].target.index,
                                    children:[]
                                });
                                pos[data[i].target.index]=posArr.concat([obj.children.length-1]);
                                data.splice(i,1);
                                i--;
                            }
                        }
                        i++;
                        if(i>data.length-1){
                            i=0;
                        }
                    }
                    return tree;
                }
                temp.children = treeData;
                middleData = temp;
            }
        });
  //使用樹狀佈局計算位置
  var tree = d3.layout.tree()
            .size([450,450]);
  var tempNodes = tree.nodes(middleData);
  //重啓佈局以改變位置
  force.start();
  force.on("tick",function(){
  //在運動前強制修改節點座標爲樹狀結構
    tempNodes.forEach(function(d,i){
    nodes[d.name].x = d.x;
    nodes[d.name].y = d.y
      });
        linkG.attr("x1", function (d) {
                        return d.source.x;
                    })
                    .attr("y1", function (d) {
                        return d.source.y+10;
                    })
                    .attr("x2", function (d) {
                        return d.target.x;
                    })
                    .attr("y2", function (d) {
                        return d.target.y+10;
                    });


                nodeG.attr("cx", function (d) {
                    return d.x
                })
                .attr("cy", function(d){
                  return d.y+10
                });
  })
  
    
}
相關文章
相關標籤/搜索