D3.js学习笔记十四:D3.js树图(Tree)的投影

2015年03月05日 javascript 暂无评论 阅读 932 views 次

构图(d3.layout)——树图(Tree)的投影(diagonal)

投影通过一系列的坐标变换获取从源数据到目标的坐标解析,通过投影,可以将树图以不同结构映射成为更加复杂的外观。

  • diagonal.source([source]):为投影指定前项。
  • diagonal.target([target]):为投影指定后项。
  • diagonal.projection([projection]):为投影指定变换函数。例如,将树投影成为横向排列:

    function projection(d) { return [d.x, d.y]; }投影成为角度+半径的径向排列:

    function projection(d) { var r = d.y, a = (d.x - 90) / 180 * Math.PI; return [r * Math.cos(a), r * Math.sin(a)]; }

横向排列树

我们先来看一下将树横向排列的代码:

<script type="text/javascript"> //图像区域大小 var w = 960; var h = 800; //定义一个Tree对象 var tree = d3.layout.tree() .size([w,h]) .separation(function(a,b) { return a.parent == b.parent ? 1 : 2;}); //定义布局方向 var diagonal = d3.svg.diagonal() .projection(function(d) { return [d.y, d.x]; }); //新建画布 var svg = d3.select("body").append("svg") .attr("width", w) .attr("height", h) .append("g"); //根据JSON数据生成树 d3.json("treeData.php", function(error, data) { //根据数据生成nodes集合 var nodes = tree.nodes(data); //获取node集合的关系集合 var links = tree.links(nodes); //为关系集合设置贝塞尔曲线连接 var link=svg.selectAll(".link") .data(links) .enter() .append("path") .attr("class", "link") .attr("d",diagonal); //根据node集合生成节点 var node = svg.selectAll(".node") .data(nodes) .enter() .append("g") .attr("class", "node") .attr("transform", function(d){ return "translate("+d.y+"," + d.x + ")";}); //为节点添加圆形标记,如果有子节点为红色,否则绿色 node.append("circle") .attr("fill",function(d){return d.children==null?"#0F0":"#F00";}) .attr("r", function(d){return d.children==null?5:d.children.length+5;}); //为节点添加说明文字 node.append("text") .attr("dx", function(d){return d.children==null?7:d.children.length+5+2;}) .attr("dy", ".4em") .text(function(d){return d.name;}); }); </script>

横向排列,将树图的横纵坐标交换一下就行了。示例代码地址:

 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>画一个树图(Tree)</title>
<script type="text/javascript" src="js/d3.js"></script>
</head>
<style>
.node circle {
stroke: steelblue;
stroke-width: 1px;
}
.node {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
</style>
<body>
<script type="text/javascript">
//图像区域大小
var w = 960;
var h = 800;
//定义一个Tree对象
var tree = d3.layout.tree()
.size([w,h])
.separation(function(a,b) { return a.parent == b.parent ? 1 : 2;});
//定义布局方向
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
//新建画布
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h)
.append("g");
//根据JSON数据生成树
d3.json("treeData.php", function(error, data) {
//根据数据生成nodes集合
var nodes = tree.nodes(data);
//获取node集合的关系集合
var links = tree.links(nodes);
//为关系集合设置贝塞尔曲线连接
var link=svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d",diagonal);
//根据node集合生成节点
var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d){ return "translate("+d.y+"," + d.x + ")";});
//为节点添加圆形标记,如果有子节点为红色,否则绿色
node.append("circle")
.attr("fill",function(d){return d.children==null?"#0F0":"#F00";})
.attr("r", function(d){return d.children==null?5:d.children.length+5;});
//为节点添加说明文字
node.append("text")
.attr("dx", function(d){return d.children==null?7:d.children.length+5+2;})
.attr("dy", ".4em")
.text(function(d){return d.name;});
});
</script>
</body>
</html>

打开后右键查看源码。

环形排列

将树映射成为一个圆环状,需要一点技巧,我们将Tree.size([宽,高])的设置更改为Tree.size([最大角度,半径]),这样,当数据被读取到树,实际上的根据树的规则返回的仍然是[x,y]的对,只不过因为我们指定X为最大角度,因此返回的X我们可以映射为角度。有了角度和半径,我们就可以根据直角三角形的正余弦公式,得到临边(x),对边(y)的值,这样就得到变换后的新点位。

我们要获取[x0,y0]的实际坐标,根据三角函数sin(a)=y0/Y;可得y0=Y*sin(a);cos(a)=x0/Y,可得x=Y*cos(a);明白了这个变换公式,那么下面的代码就容易看懂了。

<script type="text/javascript"> //图像区域大小 var R = 600; //定义一个Tree对象,定义旋转角度和最大半径 var tree = d3.layout.tree() .size([360,R/2-120]) .separation(function(a,b) { return a.parent == b.parent ? 1 : 2;}); //定义布局方向 var diagonal = d3.svg.diagonal() .projection(function(d) { var r = d.y, a = (d.x-90) / 180 * Math.PI; return [r * Math.cos(a), r * Math.sin(a)]; }); //新建画布,移动到圆心位置 var svg = d3.select("body").append("svg") .attr("width", R) .attr("height", R) .append("g") .attr("transform", function(d){ return "translate("+R/2+"," + R/2 + ")";}); //根据JSON数据生成树 d3.json("treeData.php", function(error, data) { //根据数据生成nodes集合 var nodes = tree.nodes(data); //获取node集合的关系集合 var links = tree.links(nodes); //为关系集合设置贝塞尔曲线连接 var link=svg.selectAll(".link") .data(links) .enter() .append("path") .attr("class", "link") .attr("d",diagonal); //根据node集合生成节点 var node = svg.selectAll(".node") .data(nodes) .enter() .append("g") .attr("class", "node") .attr("transform",function(d){return "rotate(" + (d.x-90) + ")translate(" + d.y + ")"; }); //为节点添加圆形标记,如果有子节点为红色,否则绿色 node.append("circle") .attr("fill",function(d){return d.children==null?"#0F0":"#F00";}) .attr("r", 5); //为节点添加说明文字 node.append("text") .attr("dy", ".4em") .text(function(d){return d.name;}) .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) .attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; }); }); </script>

1、tree.size([360,R/2-120])的意思是树展开最大角度为360度,半径为R-120,-120的意思是为了给节点标签预留120像素的宽度。

2、diagonal.projection求得坐标变换后的新位置,-90度是因为我们要将节点标签位置作调整:左侧的标签翻转,这样容易阅读,可是默认的0度是从原点的一条横线,我们将它减掉90度,让它从原点顶部开始计算,这样可以用大于或者小于180度判断节点是在左侧还是右侧:

3、根据node集合生成节点的-90度解释同上,可以理解为:统一将节点按照距离排放好,然后旋转不同的角度。

4、为节点添加说明文字,大于180度,标签翻转。

示例代码地址:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>画一个树图(Tree)</title>
<script type="text/javascript" src="js/d3.js"></script>
</head>
<style>
.node circle {
stroke: steelblue;
stroke-width: 1px;
}
.node {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
</style>
<body>
<script type="text/javascript">
//图像区域大小
var R = 600;
//定义一个Tree对象,定义旋转角度和最大半径
var tree = d3.layout.tree()
.size([360,R/2-120])
.separation(function(a,b) { return a.parent == b.parent ? 1 : 2;});
//定义布局方向
var diagonal = d3.svg.diagonal()
.projection(function(d) {
var r = d.y, a = (d.x-90) / 180 * Math.PI;
return [r * Math.cos(a), r * Math.sin(a)];
});
//新建画布,移动到圆心位置
var svg = d3.select("body").append("svg")
.attr("width", R)
.attr("height", R)
.append("g")
.attr("transform", function(d){ return "translate("+R/2+"," + R/2 + ")";});
//根据JSON数据生成树
d3.json("treeData.php", function(error, data) {
//根据数据生成nodes集合
var nodes = tree.nodes(data);
//获取node集合的关系集合
var links = tree.links(nodes);
//为关系集合设置贝塞尔曲线连接
var link=svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d",diagonal);
//根据node集合生成节点
var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform",function(d){return "rotate(" + (d.x-90) + ")translate(" + d.y + ")"; });
//为节点添加圆形标记,如果有子节点为红色,否则绿色
node.append("circle")
.attr("fill",function(d){return d.children==null?"#0F0":"#F00";})
.attr("r", 5);
//为节点添加说明文字
node.append("text")
.attr("dy", ".4em")
.text(function(d){return d.name;})
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; });
});
</script>
</body>
</html>

,打开后右键查看源码。

JSON数据:php用来生成树的数据,参照:http://www.daliane.com/d3_js_xue_xi_bi_ji_shi_san_d3_js_shu_tu_tree/。

给我留言

您必须 登录 才能发表留言!

Copyright © 大一网 保留所有权利.  

用户登录

分享到: