本節(jié)內(nèi)容將描述餅狀圖、力導(dǎo)向圖仓手、弦圖胖齐、集群圖、樹(shù)狀圖俗或、打包圖市怎、分區(qū)圖、圓形分區(qū)圖辛慰、直方圖区匠、捆圖、堆棧圖帅腌、矩陣樹(shù)圖驰弄、地圖的繪制過(guò)程,參考D3.js入門(mén)系列
溫馨提示:對(duì)于有D3基礎(chǔ)的人速客,本節(jié)內(nèi)容能夠幫助其快速掌握各圖表的繪制戚篙。若沒(méi)有掌握基礎(chǔ)知識(shí),不建議直接學(xué)習(xí)本節(jié)內(nèi)容溺职。
對(duì)圖表繪制的重點(diǎn)內(nèi)容進(jìn)行了總結(jié)岔擂,下述圖表繪制步驟相似位喂,總結(jié)如下:
1.使用布局,轉(zhuǎn)換數(shù)據(jù)格式
2.如需繪制復(fù)雜path乱灵,需要?jiǎng)?chuàng)建對(duì)應(yīng)的路徑生成器
3.依次繪制各個(gè)元素塑崖,如需繪制path可能需要調(diào)用路徑生成器
布局內(nèi)容總結(jié):
D3總共提供了12個(gè)布局:餅狀圖(Pie)、力導(dǎo)向圖(Force)痛倚、弦圖(Chord)规婆、樹(shù)狀圖(Tree)、集群圖(Cluster)蝉稳、捆圖(Bundle)抒蚜、打包圖(Pack)、直方圖(Histogram)耘戚、分區(qū)圖(Partition)嗡髓、堆棧圖(Stack)、矩陣樹(shù)圖(Treemap)毕莱、層級(jí)圖(Hierarchy)器贩。
12 個(gè)布局中,層級(jí)圖(Hierarchy)不能直接使用朋截。集群圖蛹稍、打包圖、分區(qū)圖部服、樹(shù)狀圖唆姐、矩陣樹(shù)圖是由層級(jí)圖擴(kuò)展來(lái)的。如此一來(lái)廓八,能夠使用的布局是11個(gè)(有5個(gè)是由層級(jí)圖擴(kuò)展而來(lái))奉芦。這些布局的作用都是將某種數(shù)據(jù)轉(zhuǎn)換成另一種數(shù)據(jù),而轉(zhuǎn)換后的數(shù)據(jù)是利于可視化的剧蹂。
餅狀圖
1.數(shù)據(jù)格式
var dataset = [ 30 , 10 , 43 , 55 , 13 ];
2.使用pie布局,轉(zhuǎn)換數(shù)據(jù)
var pie = d3.layout.pie();
var piedata = pie(data);
轉(zhuǎn)換后的數(shù)據(jù):
data声功、startAngle、endAngle宠叼、padAngle先巴、value
3.繪制
弧生成器計(jì)算路徑(svg的path)
var arc = d3.svg.arc() //弧生成器
.innerRadius(innerRadius) //設(shè)置內(nèi)半徑
.outerRadius(outerRadius); //設(shè)置外半徑
繪制路徑path,需要調(diào)用弧生成器
.attr("d", function(d){
return arc(d); //調(diào)用弧生成器冒冬,得到路徑值
})
或者
.attr("d", arc)
繪制文本text伸蚯,計(jì)算路徑中心位置,放入文本值
.attr("transform",function(d){
return "translate(" + arc.centroid(d) + ")";
})
力導(dǎo)向圖
1.數(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 } ];
2.force布局简烤,轉(zhuǎn)換數(shù)據(jù)
var force = d3.layout.force()
.nodes(nodes) //指定節(jié)點(diǎn)數(shù)組
.links(edges) //指定連線數(shù)組
.size([width,height]) //指定作用域范圍
.linkDistance(150) //指定連線長(zhǎng)度
.charge([-400]); //相互之間的作用
轉(zhuǎn)換后的數(shù)據(jù):
節(jié)點(diǎn):index(節(jié)點(diǎn)的索引號(hào))剂邮、px、py(節(jié)點(diǎn)上一時(shí)刻的坐標(biāo))横侦、x挥萌、y(現(xiàn)在的坐標(biāo))绰姻、weight(節(jié)點(diǎn)權(quán)重)、name
連線:source瑞眼、target
然后龙宏,使力學(xué)作用生效:
force.start(); //開(kāi)始作用
3.繪制
- line,連線
- circle,節(jié)點(diǎn)
- text,文字
繪制節(jié)點(diǎn)時(shí)棵逊,使節(jié)點(diǎn)拖動(dòng)
.call(force.drag); //使得節(jié)點(diǎn)能夠拖動(dòng)
力導(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; });
});
弦圖
1.數(shù)據(jù)格式
var city_name = [ "北京" , "上海" , "廣州" , "深圳" , "香港" ];
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 ]
];
2.chord布局
var chord_layout = d3.layout.chord()
.padding(0.03) //節(jié)點(diǎn)之間的間隔
.sortSubgroups(d3.descending) //排序
.matrix(population); //輸入矩陣
數(shù)據(jù)轉(zhuǎn)換
var groups = chord_layout.groups();
var chords = chord_layout.chords();
轉(zhuǎn)換后的數(shù)據(jù):
節(jié)點(diǎn):angle、startAngle蛙讥、endAngle锯蛀、index、name次慢、value
連線: source旁涤、target
3.繪制
首先繪制外部圓環(huán),使用arc弧生成器
var outer_arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
繪制path迫像,使用數(shù)據(jù)為groups
.attr("d", outer_arc) //調(diào)用arc路徑生成器
繪制text,使用數(shù)據(jù)為groups劈愚。首先旋轉(zhuǎn)適當(dāng)角度,然后向上移動(dòng)外半徑個(gè)長(zhǎng)度闻妓,135-225度范圍的文字倒置
.each( function(d,i) {
d.angle = (d.startAngle + d.endAngle) / 2;
d.name = city_name[i];
})
.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)" : "");
})
然后菌羽,繪制弦,使用chord生成器生成路徑
var inner_chord = d3.svg.chord()
.radius(innerRadius);
繪制path,使用數(shù)據(jù)為chords
.attr("d", inner_chord) //調(diào)用chord路徑生成器
集群圖
1.數(shù)據(jù)格式
{
"name":"中國(guó)",
"children":
[
{
"name":"浙江" ,
"children":
[
{"name":"杭州" },
{"name":"寧波" },
{"name":"溫州" },
{"name":"紹興" }
]
},
{
"name":"廣西" ,
"children":
[
{"name":"桂林"},
{"name":"南寧"},
{"name":"柳州"},
{"name":"防城港"}
]
},
......
]
}
2.定義集群圖布局由缆,轉(zhuǎn)換數(shù)據(jù)
var cluster = d3.layout.cluster()
.size([width, height - 200]); //設(shè)定尺寸
{% endhighlight %}
使用數(shù)據(jù)
d3.json("city.json", function(error, root) {
var nodes = cluster.nodes(root);
var links = cluster.links(nodes);
}
轉(zhuǎn)換后的數(shù)據(jù):
節(jié)點(diǎn):children(Array)注祖、depth、name均唉、x是晨、y
連線:source、target
3.繪制
首先舔箭,創(chuàng)建對(duì)角線生成器
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
繪制連線罩缴,使用生成器
.attr("d", diagonal) //使用對(duì)角線生成器
然后繪制節(jié)點(diǎn)circle。
樹(shù)狀圖
和集群圖寫(xiě)法基本相同限嫌,使用布局不同靴庆。
var tree = d3.layout.tree()
.size([width, height-200])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2); });
轉(zhuǎn)換后的數(shù)據(jù)與集群圖相同。
打包圖
數(shù)據(jù)格式與樹(shù)狀圖相同怒医,布局如下:
var pack = d3.layout.pack()
.size([ width, height ])
.radius(20);
數(shù)據(jù)轉(zhuǎn)換寫(xiě)法也類似炉抒。
轉(zhuǎn)換后的數(shù)據(jù):
節(jié)點(diǎn):children、depth稚叹、name焰薄、r(半徑)拿诸、value、x塞茅、y(圓心位置)
連線:source亩码、target
然后分別繪制circle和text。
分區(qū)圖
數(shù)據(jù)類型與集群圖野瘦、樹(shù)狀圖描沟、打包圖相同。用于表示包含與被包含關(guān)系的鞭光。布局如下:
var partition = d3.layout.partition()
.sort(null) //sort設(shè)定內(nèi)部的頂點(diǎn)的排序函數(shù)吏廉,null表示不排序
.size([width,height]) //size設(shè)定轉(zhuǎn)換后圖形的范圍
.value(function(d) { return 1; }); //value設(shè)定表示分區(qū)大小的值
value設(shè)定表示分區(qū)大小的值。這里的意思是:如果數(shù)據(jù)文件中用size值表示結(jié)點(diǎn)大小惰许,那么這里可寫(xiě)成return d.size席覆。
數(shù)據(jù)轉(zhuǎn)換寫(xiě)法也類似。(nodes汹买、links)
轉(zhuǎn)換后的數(shù)據(jù):
節(jié)點(diǎn):children佩伤、depth、dx(寬度)晦毙、dy(高度)生巡、name、value结序、x障斋、y(坐標(biāo))
連線:source、target
然后繪制rect和text徐鹤。rect的width垃环、height屬性分別對(duì)應(yīng)數(shù)據(jù)屬性dx、dy返敬。
圓形分區(qū)圖
1.數(shù)據(jù)格式
與前面相同遂庄。
2.布局
var partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value(function(d) { return 1; });
數(shù)據(jù)轉(zhuǎn)換寫(xiě)法也類似。(nodes劲赠、links)
轉(zhuǎn)換后的數(shù)據(jù):
節(jié)點(diǎn):children涛目、depth、dx(角度)凛澎、dy(向外寬度)霹肝、name、value塑煎、x(繞圓心方向的起始位置)沫换、y(由圓心向外方向的起始位置)
連線:source、target
3.繪制
分別繪制path和text最铁。
首先創(chuàng)建弧生成器讯赏。
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return Math.sqrt(d.y); })
.outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
繪制path使調(diào)用弧生成器垮兑。
.attr("d", arc)
繪制text時(shí)要進(jìn)行一下轉(zhuǎn)換:
.attr("transform",function(d,i){
//第一個(gè)元素(最中間的),只平移不旋轉(zhuǎn)
if( i == 0 )
return "translate(" + arc.centroid(d) + ")";
//其他的元素漱挎,既平移也旋轉(zhuǎn)
var r = 0;
if( (d.x+d.dx/2)/Math.PI*180 < 180 ) // 0 - 180 度以內(nèi)的
r = 180 * ((d.x + d.dx / 2 - Math.PI / 2) / Math.PI);
else // 180 - 360 度以內(nèi)的
r = 180 * ((d.x + d.dx / 2 + Math.PI / 2) / Math.PI);
//既平移也旋轉(zhuǎn)
return "translate(" + arc.centroid(d) + ")" +
"rotate(" + r + ")";
})
直方圖
1.數(shù)據(jù)格式:數(shù)組
2.布局
var histogram = d3.layout.histogram()
.range([-50, 50]) //區(qū)間的范圍
.bins(bins_num) //矩形數(shù)量
.frequency(true); //若值為true系枪,則統(tǒng)計(jì)的是個(gè)數(shù);若值為false磕谅,則統(tǒng)計(jì)的是概率
數(shù)據(jù)轉(zhuǎn)換
var data = histogram(dataset);
轉(zhuǎn)換后的數(shù)據(jù):
x(區(qū)間的起始位置)私爷、dx(區(qū)間的寬度)、y(
如果frequency為true怜庸,則是落到此區(qū)間的數(shù)值的數(shù)量当犯;如果frequency為false,則是落到此區(qū)間的概率)
3.繪制
矩形rect割疾、坐標(biāo)軸line、刻度line嘉栓、文字text宏榕。
捆圖
1.數(shù)據(jù)格式
var cities = {
name: "",
children:[
{name: "北京"},{name: "上海"},{name: "杭州"},
{name: "廣州"},{name: "桂林"},{name: "昆明"},
{name: "成都"},{name: "西安"},{name: "太原"}
]
};
var railway = [
{source: "北京", target: "上海"},
{source: "北京", target: "廣州"},
{source: "北京", target: "杭州"},
{source: "北京", target: "西安"},
{source: "北京", target: "成都"},
{source: "北京", target: "太原"},
{source: "北京", target: "桂林"},
{source: "北京", target: "昆明"},
{source: "北京", target: "成都"},
{source: "上海", target: "杭州"},
{source: "昆明", target: "成都"},
{source: "西安", target: "太原"}
];
2.布局
需要使用集群圖布局和捆圖布局。
var cluster = d3.layout.cluster()
.size([360, width/2-50]); //首先使用集群圖布局侵佃,360表示角度麻昼,width/2-50 表示捆圖圓半徑
var bundle = d3.layout.bundle(); //捆圖布局
var nodes = cluster.nodes(cities); //數(shù)據(jù)轉(zhuǎn)換
console.log(nodes);
function map(nodes, links) {
var hash = [];
for(var i=0; i<nodes.length; i++) {
hash[nodes[i].name] = nodes[i];
}
var resultLinks = [];
for(var i=0; i<links.length; i++) {
resultLinks.push({source:hash[links[i].source], target:hash[links[i].target]});
}
return resultLinks;
}
var oLinks = map(nodes, railway);
console.log(oLinks);
var links = bundle(oLinks); //數(shù)據(jù)轉(zhuǎn)換
console.log(links);
轉(zhuǎn)換后的數(shù)據(jù):
nodes-節(jié)點(diǎn):name、depth馋辈、parent抚芦、x、y
oLinks-節(jié)點(diǎn)連線:source迈螟、target
links-捆圖線條數(shù)據(jù):Array(Object(name叉抡、depth、parent答毫、children褥民、x、y))
3.繪制
創(chuàng)建放射式的線段生成器:
var line = d3.svg.line.radial()
.interpolate("bundle") //在line.interpolate()所預(yù)定義的插值模式中洗搂,有一種就叫做bundle消返,正是為捆圖準(zhǔn)備的。
.tension(.85) //拉伸度0.85耘拇。變小線的彎曲度會(huì)變小撵颊,會(huì)使線與線之間的距離增大
.radius(function(d) {return d.y;})
.angle(function(d) {return d.x/180*Math.PI;})
首先繪制path,調(diào)用線段生成器:
.attr("d", line); //調(diào)用線段生成器
接著創(chuàng)建g惫叛,在g中繪制circle和text
繪制g需要旋轉(zhuǎn)倡勇、平移:
.attr("transform", function(d) {
return "rotate("+(d.x-90)+")"+
"translate("+(0, d.y)+")";
});
堆棧圖
1.數(shù)據(jù)格式
var dataset = [
{ name: "PC" ,
sales: [ { year:2005, profit: 3000 },
{ year:2006, profit: 1300 },
{ year:2007, profit: 3700 },
{ year:2008, profit: 4900 },
{ year:2009, profit: 700 }] },
{ name: "SmartPhone" ,
sales: [ { year:2005, profit: 2000 },
{ year:2006, profit: 4000 },
{ year:2007, profit: 1810 },
{ year:2008, profit: 6540 },
{ year:2009, profit: 2820 }] },
{ name: "Software" ,
sales: [ { year:2005, profit: 1100 },
{ year:2006, profit: 1700 },
{ year:2007, profit: 1680 },
{ year:2008, profit: 4000 },
{ year:2009, profit: 4900 }]}
];
2.布局
創(chuàng)建堆棧圖布局
var stack = d3.layout.stack()
.values(function(d) {return d.sales;})
.x(function(d) {return d.year;})
.y(function(d) {return d.profit});
數(shù)據(jù)轉(zhuǎn)換
var data = stack(dataset);
轉(zhuǎn)換后的數(shù)據(jù):
name、Array(y0(縱坐標(biāo))挣棕、y(高度)译隘、(year亲桥、profit)(屬性名由dataset屬性名決定))
3.繪制
分別繪制矩形rect、圓形circle固耘、文字text题篷、坐標(biāo)軸axis,繪制過(guò)程與柱形圖相似厅目。
矩陣樹(shù)圖
1.數(shù)據(jù)格式
var dataSet = {
"name": "中國(guó)",
"children":
[
{
"name": "浙江",
"children":
[
{"name":"杭州", "gdp":8343},
{"name":"寧波", "gdp":7128},
{"name":"溫州", "gdp":4003},
{"name":"紹興", "gdp":3620},
{"name":"湖州", "gdp":1803},
{"name":"嘉興", "gdp":3147},
{"name":"金華", "gdp":2958},
{"name":"衢州", "gdp":1056},
{"name":"舟山", "gdp":1021},
{"name":"臺(tái)州", "gdp":3153},
{"name":"麗水", "gdp":983}
]
},
......
]
}
2.布局
創(chuàng)建矩陣樹(shù)圖布局
var treemap = d3.layout.treemap()
.size([width, height])
.value(function(d) {return d.gdp;})
數(shù)據(jù)轉(zhuǎn)換
var nodes = treemap.nodes(dataSet);
var links = treemap.links(nodes);
轉(zhuǎn)換后的數(shù)據(jù):
節(jié)點(diǎn):parent番枚、children、depth损敷、value葫笼、x墨林、y漾肮、dx、dy
連線:source评腺、target
3.繪制
分別繪制矩形rect诱桂、文字text洋丐。
地圖
1.投影,轉(zhuǎn)換經(jīng)緯度數(shù)據(jù)挥等,使能夠在二維平面上顯示
var projection = d3.geo.mercator() //投影函數(shù)
.center([107, 31]) //地圖中心位置友绝,經(jīng)緯度
.scale(850) //縮放比例
.translate([width/2, height/2]);
2.創(chuàng)建地圖路徑生成器
var path = d3.geo.path()
.projection(projection);
3.繪制
.data(root.features) //數(shù)據(jù)綁定
...
.attr("d", path) //調(diào)用路徑生成器
更多內(nèi)容:Github個(gè)人博客
備注:本文發(fā)表于 https://cnyangkui.github.io/2017/11/15/d3-graphlist/