「前言」
最開(kāi)始的初衷是想畫(huà)個(gè)弦圖(chord)與尚ǎ基圖(sankey)谱轨,真的很炫有沒(méi)有救湖!然而D3零基礎(chǔ)的我表示源碼看不懂拣播,受到1萬(wàn)點(diǎn)暴擊(+﹏+)~ 于是果斷去惡補(bǔ)D3的基礎(chǔ)知識(shí)晾咪,并加以整理同時(shí)加深對(duì)自己的印象。
「基礎(chǔ)概念」
選擇集
使用 d3.select() 或 d3.selectAll() 選擇元素后返回的對(duì)象贮配,就是選擇集
無(wú)名函數(shù)
function(d, i) 這個(gè)函數(shù)以后經(jīng)常要使用到
- d 代表數(shù)據(jù)谍倦,也就是與某元素綁定的數(shù)據(jù)。
- i 代表索引泪勒,代表數(shù)據(jù)的索引號(hào)昼蛀,從 0 開(kāi)始。
「數(shù)據(jù)綁定」
D3可以用兩種函數(shù)來(lái)綁定數(shù)據(jù):
- datum(): 綁定一個(gè)數(shù)據(jù)到選擇集上圆存,這里的數(shù)據(jù)并非一定要是number(數(shù)值型)叼旋,也可以是string(字符串)、bollean(布爾型)和object(對(duì)象)
- data(): 綁定一個(gè)數(shù)組到選擇集上沦辙,數(shù)組的各項(xiàng)值分別與選擇集的各元素綁定夫植,更常用
data() 函數(shù)的常用語(yǔ)法
var dataset = [10,20,30,40,50];
var body = d3.select("body");
body.selectAll("p") //選擇body中的所有p,但是目前還沒(méi)有油讯,所以是空集
.data(dataset) //綁定數(shù)組
.enter() //指定選擇集的enter部分
.append("p")
.text(function(d){
return d;
})
這里要解釋下 Enter 的概念详民,它與Update、Exit是D3中三個(gè)非常重要的概念陌兑,處理的是當(dāng)選擇集和數(shù)據(jù)的數(shù)量關(guān)系不確定的情況沈跨。
如果數(shù)組為 [3, 6, 9, 12, 15],將此數(shù)組綁定到三個(gè) p 元素的選擇集上兔综《隽荩可以想象狞玛,會(huì)有兩個(gè)數(shù)據(jù)沒(méi)有元素與之對(duì)應(yīng),這時(shí)候 D3 會(huì)建立兩個(gè)空的元素與數(shù)據(jù)對(duì)應(yīng)涧窒,這一部分就稱(chēng)為 Enter心肪。而有元素與數(shù)據(jù)對(duì)應(yīng)的部分稱(chēng)為 Update。如果數(shù)組為 [3]杀狡,則會(huì)有兩個(gè)元素沒(méi)有數(shù)據(jù)綁定蒙畴,那么沒(méi)有數(shù)據(jù)綁定的部分被稱(chēng)為 Exit。示意圖如下所示呜象。
「柱形圖」
Bar Chart一般包括:矩形膳凝、坐標(biāo)軸與文字。
矩形
這里我們直接定義一個(gè)數(shù)組恭陡,用數(shù)組項(xiàng)對(duì)應(yīng)矩形的長(zhǎng)短(然而這種方法并不理想)蹬音。
var dataset = [50, 43, 120, 87, 99, 167, 142];
定義一塊SVG的繪制區(qū)域:
var width = 600; // SVG的寬度
var height = 600; // SVG的長(zhǎng)度
var svg = d3.select("body")
.append('svg') // body中添加SVG
.attr('width', width)
.attr('height', height);
定義三個(gè)我們要用的變量
var padding = {top: 20, right: 20, bottom: 20, left: 20};
var rectStep = 35;
var rectWidth = 30;
padding是svg內(nèi)的最外一層區(qū)域,留一段空白寬度是為了防止圖形繪制帶svg區(qū)域外休玩。rectStep表示前一個(gè)矩形到下一個(gè)矩形的距離(包括空白間隔)著淆,而rectWidth是矩形實(shí)際的寬度。說(shuō)了這么多還是看圖更易懂:
添加矩形元素
var rect = svg.selectAll("rect")
.data(dataset)
.enter() //獲取enter部分
.append("rect") //添加rect元素拴疤,使其與綁定數(shù)組的長(zhǎng)度一致
.attr("fill","steelblue")
.attr("x",function(d,i){ //設(shè)置X坐標(biāo)
return padding.left + i * rectStep;
})
.attr("y",function(d,i){ //設(shè)置Y坐標(biāo)
return height - padding.bottom - d;
})
.attr("width",rectWidth) //設(shè)置矩形寬度永部,之前定義的
.attr("height",function(d){ //設(shè)置矩形高度,即為數(shù)組中的各項(xiàng)值
return d
});
因?yàn)閿?shù)組dataset的長(zhǎng)度為7呐矾,所以最后生成7個(gè)矩形苔埋。x與y坐標(biāo)是矩形的左上角頂點(diǎn)。 這個(gè)坐標(biāo)是相對(duì)應(yīng)svg繪圖區(qū)域來(lái)講的蜒犯,坐標(biāo)原點(diǎn)位于左上角(0,0)组橄。
一張圖直接說(shuō)明:
標(biāo)簽文字
var text = svg.selectAll(text)
.data(dataset)
.enter()
.append("text")
.attr("fill","white")
.attr("font-size","14px")
.attr("text-anchor","middle")
.attr("x",function(d,i){ //與矩形的X坐標(biāo)一樣
return padding.left + i * rectStep;
})
.attr("y",function(d){
return height - padding.bottom - d;
})
.attr('dx', rectWidth/2) //x軸相對(duì)平移距離
.attr('dy', "1em") //em單位表示的是當(dāng)前文字所占一行的高度
.text(function(d){ //要顯示的文字內(nèi)容
return d;
});
添加文字標(biāo)簽的方法與添加矩形元素方法相類(lèi)似,不過(guò)顏色要與矩形的顏色區(qū)分罚随。通過(guò)設(shè)置元素的text-anchor玉工、x、y淘菩、dx與dy五個(gè)屬性遵班,讓文字顯示在每個(gè)矩形的正中心。
其中dx,dy表示相對(duì)(x,y)平移的大小潮改,所以文本會(huì)從(x+dx,y+dy)位置開(kāi)始顯示费奸,這個(gè)位置也叫<u>起始位置</u>。屬性text-anchor有三個(gè)值:start进陡、middle、end,微服,這里用middle表示文字中心位于<u>起始位置</u>上趾疚。
還是上圖說(shuō)明問(wèn)題:
效果圖:
坐標(biāo)軸
坐標(biāo)軸的主直線(xiàn)由path構(gòu)成缨历,刻度由line繪制,刻度文字用text完成糙麦。之前我們直接用數(shù)值的大小來(lái)表示像素的大小辛孵,這里我們使用比例尺,定義如下:
// SVG畫(huà)布
var width = 600;
var height = 600;
var svg = d3.select("body").append('svg')
.attr('width', width)
.attr('height', height);
// 坐標(biāo)軸的線(xiàn)性比例尺
var xScale = d3.scale.linear()
.domain([0,10]) //定義域
.range([0,300]); //值域
// 定義坐標(biāo)軸
var axis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(5); //刻度的數(shù)量赡磅,這里顯示5個(gè)
//在 SVG 中添加一個(gè)分組元素魄缚,再將坐標(biāo)軸的其他元素添加到里面
var gAxis = svg.append("g")
.attr("transform","translate(80,80)");
.call(axis)
gAxis.attr('class', 'axis'); //添加一些樣式,否則太太太丑了...
call()函數(shù)的使用十分常見(jiàn)焚廊,這里使用的參數(shù)是前面定義的坐標(biāo)軸axis冶匹,等價(jià)于axis(gAxis);
的形式。效果圖如下:
「柱形圖的坐標(biāo)軸」
對(duì)初學(xué)者而言咆瘟,這里的坑更多(老司機(jī)請(qǐng)無(wú)視)嚼隘。主要是因?yàn)槭褂昧吮壤咧螅琗Y坐標(biāo)軸袒餐、矩形長(zhǎng)寬飞蛹、刻度都要與之相對(duì)應(yīng)。不要問(wèn)我為什么知道這么多灸眼,都是淚......
為矩形圖定義比例尺
var xAxisWidth = 300; //x軸寬度
var yAxisWidth = 300; //y軸寬度
var xScale = d3.scale.ordinal() //x軸比例尺(序數(shù)比例尺)
.domain(d3.range(dataset.length))
.rangeRoundBands([0,xAxisWidth],0.2);
var yScale = d3.scale.linear() //y軸比例尺(線(xiàn)性比例尺)
.domain([0,d3.max(dataset)])
.range([0,yAxisWidth]);
定義完比例尺之后卧檐,矩形的高度、位置都要用比例尺來(lái)計(jì)算焰宣。如此之后霉囚,僅需簡(jiǎn)單修改比例尺,圖表就能自動(dòng)伸縮宛徊,所以前面的<u>矩形元素</u>與<u>矩形文字</u>的代碼都需要修改
矩形元素修改部分
.attr("x",function(d,i){
return padding.left + xScale(i); // return padding.left + i * rectStep;
})
.attr("y",function(d,i){
return height - padding.bottom - yScale(d); // return height - padding.bottom - d;
})
.attr("width",xScale.rangeBand())
.attr("height",function(d){
return yScale(d);
})
標(biāo)簽文字修改部分
.attr("x",function(d,i){ //與矩形的X坐標(biāo)一樣
return padding.left + xScale(i);
})
.attr("y",function(d){
return height - padding.bottom - yScale(d);
})
.attr('dx', xScale.rangeBand()/2) //x軸相對(duì)平移距離
.attr('dy', "1em") //em單位表示的是當(dāng)前文字所占一行的高度
.text(function(d){ //要顯示的文字內(nèi)容
return d;
});
定義坐標(biāo)軸
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
yScale.range([yAxisWidth,0]); //值域相反
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
此外還要注意佛嬉,y軸坐標(biāo)的值域要與原來(lái)相反,從最大值到最小值闸天,否則最后會(huì)出現(xiàn)下面這種情況:
<img src="http://upload-images.jianshu.io/upload_images/4762054-04b43eb28412b558.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" style="width: 35px;"/>
添加坐標(biāo)軸元素
//添加x軸
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
//添加y軸
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom - yAxisWidth) + ")")
.call(yAxis);
這里要小心x軸暖呕、y軸平移到目標(biāo)位置的距離,以及你設(shè)置padding前后左右的寬度苞氮,防止坐標(biāo)軸跑到外面去(又是血與淚的教訓(xùn))湾揽。
最后效果圖:
完整源代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text{
font-family: sans-serif;
font-size: 11px;
}
</style>
</head>
<body>
<script >
// 添加SVG畫(huà)布
var dataset = [50, 43, 120, 87, 99, 167, 142];
var width = 600; // SVG的寬度
var height = 600; // SVG的長(zhǎng)度
var svg = d3.select("body")
.append('svg') // body中添加SVG
.attr('width', width)
.attr('height', height);
var padding = {top: 20, right: 20, bottom: 20, left: 30};
// 定義數(shù)據(jù)與比例尺
var xAxisWidth = 300; //x軸寬度
var yAxisWidth = 300; //y軸寬度
var xScale = d3.scale.ordinal() //x軸比例尺(序數(shù)比例尺)
.domain(d3.range(dataset.length))
.rangeRoundBands([0,xAxisWidth],0.2);
var yScale = d3.scale.linear() //y軸比例尺(線(xiàn)性比例尺)
.domain([0,d3.max(dataset)])
.range([0,yAxisWidth]);
// 添加矩形和文字元素
var rect = svg.selectAll("rect")
.data(dataset)
.enter() //獲取enter部分
.append("rect") //添加rect元素,使其與綁定數(shù)組的長(zhǎng)度一致
.attr("fill","steelblue")
.attr("x",function(d,i){ //設(shè)置X坐標(biāo)
// return padding.left + i * rectStep;
return padding.left + xScale(i);
})
.attr("y",function(d,i){ //設(shè)置Y坐標(biāo)
// return height - padding.bottom - d;
return height - padding.bottom - yScale(d);
})
.attr("width",xScale.rangeBand()) //設(shè)置矩形寬度
.attr("height",function(d){
return yScale(d);
})
var text = svg.selectAll(text)
.data(dataset)
.enter()
.append("text")
.attr("fill","white")
.attr("font-size","14px")
.attr("text-anchor","middle")
.attr("x",function(d,i){ //與矩形的X坐標(biāo)一樣
return padding.left + xScale(i);
})
.attr("y",function(d){
return height - padding.bottom - yScale(d);
})
.attr('dx', xScale.rangeBand()/2) //x軸相對(duì)平移距離
.attr('dy', "1em") //em單位表示的是當(dāng)前文字所占一行的高度
.text(function(d){ //要顯示的文字內(nèi)容
return d;
});
// 定義坐標(biāo)軸
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
yScale.range([yAxisWidth,0]);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
// 添加坐標(biāo)軸
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom - yAxisWidth) + ")")
.call(yAxis);
</script>
</body>
</html>
「參考資料」
Learning D3.JS
D3.js:Update笼吟、Enter库物、Exit
D3.js - 初體驗(yàn)
D3.js數(shù)據(jù)可視化系列教程