餅狀圖是資料統計中經常用到的另一類圖表,餅圖可以直觀地顯示一個資料系列中各項的大小與各項總和的比例,本文將使用D3上手製作一個簡單的餅狀圖
什麼是佈局
佈局是D3中非常重要的內容,有了佈局D3才能畫出複雜的向量圖。但佈局並不是直接繪製圖形,只是將初始資料轉換成容易畫圖的圖形語言,畫圖工具能讀懂圖形語言來進行繪製。
在繪製餅狀圖中,例如有一組資料[1, 2, 3],只依靠這些資料是畫不出的,需要將這些資料轉化為圓形的起始角度和終止角度,第一塊的角度區域為[0, π/3],第二塊的角度區域為[π/3, π]……繪製工具能根據這些角度值進行繪製。佈局只進行資料轉換
D3還提供其他常用圖表的佈局函式,比如力導向圖(Force)、弦圖(Chord)、樹狀圖(Tree)、直方圖(Histogram)、分割槽圖(Partition)等,每種佈局有對應的API
確定資料
表格資料是來自w3schools的2016年9月的瀏覽器使用率
瀏覽器 | 佔比 |
---|---|
Chrome | 72.5% |
IE | 5.3% |
Firefox | 16.3% |
Safari | 3.5% |
Opera | 1.0% |
Others | 1.4% |
對此設定以下二維陣列裝載這些資料
var dataset = [['Chrome', 72.5], ['IE', 5.3], ['Firefox', 16.3], ['Safari', 3.5], ['Opera', 1.0], ['Others', 1.4]];
複製程式碼
建立佈局
- d3.pie() 構造一個新的餅佈局
構建一個餅佈局並接收資料
var pie = d3.pie()
.sort(null) // 在建立佈局的時候,預設排序資料從大到小,設為null可以按照陣列預設順序排序
.value(function(d){
return d[1]
});
複製程式碼
把資料轉換為餅圖繪製所需要的資料
var piedata = pie(dataset);
console.log(piedata);
複製程式碼
在控制檯輸出piedata,看看轉換後的資料
可以看到,原來的資料已經被轉換成6組適合畫圖的資料
- data —— 保留原有資料
- index —— 序號
- startAngle —— 一段弧的起始角度
- endAngle —— 一段弧的終止角度
生成弧生成器
雖然有了角度,但目前還是很難畫出我們想要的餅圖,還要用到D3中的路徑生成器。在svg中,path標籤的路徑數值往往十分複雜,手動去生成是不現實的,尤其是要生成各種複雜曲線的時候,好在D3提供了基本的路徑生成器,有線段、區域、弧生成器等。畫餅圖的時候需要用到弧生成器,幫助我們把轉換後的資料真正繪製出來
- d3.arc() 建立一個新的弧生成器
- arc.innerRadius([radius]) 設定內部半徑
- arc.outerRadius([radius]) 設定外部半徑
這裡把畫布的四分之一作為外部半徑,內部半徑設為0就是餅圖,沒錯,設定大於0就是圓環圖
var outerRadius = width / 4;
var innerRadius = 0;
var arc = d3.arc()
.outerRadius(outerRadius)
.innerRadius(innerRadius);
複製程式碼
繪製餅圖
跟之前一樣,建立g組合標籤後,把piedata資料繫結至g標籤,這裡要用到D3的內建顏色,d3.schemeCategory10選了10種顏色,方便我們隨時使用,為餅圖的不同區域填色,不必為想不出好的顏色搭配而煩惱,當然也可以自定義顏色
var arcs = svg.selectAll('g')
.data(piedata)
.enter()
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
// 呼叫內建顏色序列
var colors = d3.schemeCategory10;
arcs.append('path')
.attr('fill', function(d, i){
return colors[i];
})
.attr('d', function(d){
return arc(d);
});
複製程式碼
在path標籤中,給d屬性賦予資料經過弧生成器處理的值,這樣一個沒有標註的餅圖就完成了
新增標註
- arc.centroid(arguments…) 獲取弧中心座標
接下來給餅圖加一些標註,以便更好地閱讀圖形,主要用到弧中心的概念,如下圖,黑點就是每個弧的中心,利用**arc.centroid()**可以獲取弧中心的座標,並且這個座標是相對圓形的相對座標。有了這個座標,可以很方便地在每個弧上新增文字標註
arc.centroid(d)是一個包含橫座標和縱座標的陣列[x, y],給座標乘以2倍以上的數值就把文字移到圓形外邊
arcs.append('text')
.attr('transform', function(d, i){
var x = arc.centroid(d)[0] * 2.8;
var y = arc.centroid(d)[1] * 2.8;
return 'translate(' + x + ', ' + y + ')';
})
.attr('text-anchor', 'middle')
.text(function(d){
var percent = Number(d.value) / d3.sum(dataset, function(d){
return d[1];
}) * 100;
return d.data[0] + ' ' + percent.toFixed(1) + '%';
})
複製程式碼
最後再加一些連線
arcs.append('line')
.attr('stroke', 'black')
.attr('x1', function(d){ return arc.centroid(d)[0] * 2; })
.attr('y1', function(d){ return arc.centroid(d)[1] * 2; })
.attr('x2', function(d, i){
return arc.centroid(d)[0] * 2.5;
})
.attr('y2', function(d, i){
return arc.centroid(d)[1] * 2.5;
});
複製程式碼
由於最後幾個資料太小,標註文字會重疊到一起,這裡做了一個判斷處理來錯開資料值較小的文字,最後的效果圖如下所示