第四节:d3.js布局及实战¶
2.布局之饼图¶
查看饼图布局的数据¶
<script type="text/javascript" src="d3.min.js" charset="utf-8"></script>
<script type="text/javascript">
var width = 400
var height = 400
var dataset = [30, 10, 43, 55, 13]
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
//定义布局
var pie = d3.pie()
//将dataset数据按照饼图布局转换为piedata
console.log(pie)
var piedata = pie(dataset)
console.log(piedata)
</script>
布局不是要直接绘图,而是为了得到绘图所需的数据。
绘制图形¶
以上得到绘图的数据,则需要用到生成器,由于饼图的每一部分都是一段弧,所以这里用到的是弧生成器(弧的路径)。
var outerRadius = 150 //外半径,越大,圆越大
var innerRadius = 0 //内半径,这里不为0的话,在大圆中心嵌套个小圆
var arc = d3.arc() //弧生成器
.innerRadius(innerRadius) //设置内半径
.outerRadius(outerRadius) //设置外半径
//<svg>元素中添加足够的分组元素g,每一个分组用于存放一段弧的相关元素。
var arcs = svg.selectAll("g")
.data(piedata)
.enter()
.append("g")
.attr("transform", "translate(" + (width/2) + "," + (width/2) + ")")
// 为每个<g>元素,添加<path>
//v3 d3.scale.category10()
var color = d3.scaleOrdinal(d3.schemeCategory10)
arcs.append("path")
.attr("fill", function(d, i) {
return color(i)
})
.attr("d", function(d) {
return arc(d) //调用弧生成器,得到路径值
})
arcs.append("text")
.attr("transform",function(d) {
return "translate(" + arc.centroid(d) + ")"
})
.attr("text-anchor","middle")
.text(function(d) {
return d.data
})
arc.centroid(d) 能算出弧线的中心。要注意,text() 里返回的是 d.data ,而不是 d 。因为被绑定的数据是对象,里面有 d.startAngle、d.endAngle、d.data 等,其中 d.data 才是转换前的整数的值。
3.力向图¶
<script type="text/javascript" src="d3.min.js" charset="utf-8"></script>
<script type="text/javascript">
var width = 400
var height = 400
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 } ];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
//定义布局
var force = d3.forceSimulation().alphaDecay(0.1) // 设置alpha衰减系数
.force("link", d3.forceLink().distance(100)) // distance为连线的距离设置
.force("Centering", d3.forceCenter(width / 2, height / 2))
.force('collide', d3.forceCollide().radius(() => 30)) // collide 为节点指定一个radius区域来防止节点重叠。
.force("charge", d3.forceManyBody().strength(-400)) // 节点间的作用力
.nodes(nodes)
.force('link', d3.forceLink(edges))
force.restart()
console.log(nodes)
console.log(edges)
</script>
结点数据¶
边数据¶
绘制力向图¶
所需元素:
line,线段,表示连线。
circle,圆,表示节点。
text,文字,描述节点。
//定义布局
var force = d3.forceSimulation().alphaDecay(0.1) // 设置alpha衰减系数
.force("link", d3.forceLink().distance(200)) // distance为连线的距离设置
.force("Centering", d3.forceCenter(width / 2, height / 2))
.force('collide', d3.forceCollide().radius(() => 70)) // collide 为节点指定一个radius区域来防止节点重叠。
.force("charge", d3.forceManyBody().strength(-400)) // 节点间的作用力
.nodes(nodes)
.force('link', d3.forceLink(edges))
.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; });
});
force.restart()
console.log(nodes)
console.log(edges)
//添加连线
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",1)
var color = d3.scaleOrdinal(d3.schemeAccent)
//添加节点
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r",20)
.style("fill", function(d, i) {
return color(i)
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
//添加描述节点的文字
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;
});
function dragstarted(d) {
if (!d3.event.active) force.alphaTarget(0.3).restart();
d.vx = d.x;
d.vy = d.y;
}
function dragged(d) {
d.vx = d3.event.x;
d.vy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) force.alphaTarget(0);
d.vx = null;
d.vy = null;
}
力向图进阶¶
//1.结点显示图片
var nodes_img = svg.selectAll("image")
.data(root.nodes)
.enter()
.append("image")
.attr("width",img_w)
.attr("height",img_h)
.attr("xlink:href",function(d){
return d.image;
})
//更新结点图片和文字
nodes_img.attr("x",function(d){ return d.x - img_w/2; });
nodes_img.attr("y",function(d){ return d.y - img_h/2; });
{
"nodes":[
{ "name": "云天河" , "image" : "./test/tianhe.png" },
{ "name": "xx" , "image" : "./test/xx.png" },
.....
],
"edges":[
{ "source": 0 , "target": 1 , "relation":"挚友" },
{ "source": 0 , "target": 2 , "relation":"xx" },
........
]
}
//2.绘制箭头
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().scaleExtent([1, 40])
.translateExtent([
[-100, -100],
[width + 90, height + 100]
])
//添加点击结点放大效果
.on("zoom", function () {
svg.attr("transform", d3.event.transform)
})).append("g");
//箭头
svg.append('defs').append('marker')
.attr('id','end')
.attr('viewBox','-0 -5 10 10')
.attr('refX',25)
.attr('refY',0)
.attr('orient','auto')
.attr('markerWidth',10)
.attr('markerHeight',10)
.attr('xoverflow','visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#f0f')
.attr('stroke','#f0f');
4.弦图¶
弦图(Chord),主要用于表示两个节点之间的联系。 两点之间的连线,表示谁和谁具有联系。 线的粗细表示权重。
数据¶
var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港" ];
//数据 表达意思为
/*
北京 上海
北京 1000 3045
上海 3214 2000
统计人口来源
来自北京本地1000人,3045人来自上海
*/
var population = [
[ 1000, 3045 , 4567 , 1234 , 3714 ],
[ 3214, 2000 , 2060 , 124 , 3234 ],
[ 8761, 6545 , 3000 , 8045 , 647 ],
[ 3211, 1067 , 3214 , 4000 , 1006 ],
[ 2146, 1034 , 6745 , 4764 , 5000 ]
];
/*
v3写法
var chord_layout = d3.layout.chord()
.padding(0.03) //节点之间的间隔
.sortSubgroups(d3.descending) //排序
.matrix(population); //输入矩阵
*/
//v4及v5写法
var chord_layout = d3.chord()
.padAngle(0.03) //节点之间的间隔
.sortSubgroups(d3.descending)//排序
/*
matrix实际分成了两个部分,groups 和 chords,
其中groups表示弦图上的弧,称为外弦,groups中的各个元素都被计算用添加上了angle、startAngle、endAngle、index、value等字段;
chords 称为内弦,表示弦图中节点的连线及其权重。chords 里面分为 source 和 target ,分别标识连线的两端。
v3写法
var groups = chord_layout.groups();
var chords = chord_layout.chords();
console.log( groups );
console.log( chords );
*/
//v4写法
chord_layout = chord_layout(population) //输入矩阵
var groups = chord_layout.groups
var chords = chord_layout
console.log(groups)
console.log(chords)
output¶
定义相关变量¶
var width = 600
var height = 600
var innerRadius = width/2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color = d3.scaleOrdinal(d3.schemeAccent)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")")
绘制节点¶
//绘制节点(即分组,有多少个城市画多少个弧形),及绘制城市名称。
//节点位于弦图的外部。节点数组 groups 的每一项,都有起始角度和终止角度,因此节点其实是用弧形来表示的,
var outer_arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var g_outer = svg.append("g");
g_outer.selectAll("path")
.data(groups)
.enter()
.append("path")
.style("fill", function(d) { return color(d.index); })
.style("stroke", function(d) { return color(d.index); })
.attr("d", outer_arc );
/*
节点的文字(即城市名称),有两个地方要特别注意。
each():表示对任何一个绑定数据的元素,都执行后面的无名函数 function(d,i) ,函数体里做两件事:
计算起始角度和终止角度的平均值,赋值给 d.angle 。
将 city_name[i] 城市名称赋值给 d.name 。
transform 的参数:用 translate 进行坐标变换时,要注意顺序: rotate -> translate(先旋转再平移)。 此外,
( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "")
意思是,当角度在 135° 到 225° 之间时,旋转 180°。不这么做的话,下方的文字是倒的。
*/
g_outer.selectAll("text")
.data(groups)
.enter()
.append("text")
.each(function(d,i) {
d.angle = (d.startAngle + d.endAngle) / 2;
d.name = city_name[i];
})
.attr("dy",".35em")
.attr("transform", function(d){
return "rotate(" + ( d.angle * 180 / Math.PI ) + ")" + "translate(0,"+ -1.0*(outerRadius+10) +")" + ( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "");
})
.text(function(d){
return d.name;
});
绘制连线(弦)¶
var inner_chord = d3.ribbon()
.radius(innerRadius);
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chords)
.enter()
.append("path")
.attr("d", inner_chord )
.style("fill", function(d) { return color(d.source.index); })
.style("opacity", 1)
.on("mouseover",function(d,i){
d3.select(this)
.style("fill","yellow");
})
.on("mouseout",function(d,i) {
d3.select(this)
.transition()
.duration(1000)
.style("fill",color(d.source.index));
});