D3力导向图及树状布局变换
d3的力导向图是表现关系型数据比较方便且直观的方法,但是会遇到节点比较多且层级关系混乱的情况,这时树状布局就比较方便了,如何不破坏原来结构以最小的代价变换树状布局呢?下面将为大家介绍。
最终效果
http://en.jsrun.net/aXiKp/emb...
代码解析
首先我们需要准备关系型数据,数据包括节点数据nodes和连线关系数据links,links的source和target分别表示源和目标的index。
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拷贝数组,避免影响全局数据。
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
});
})
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。