一般畫統計圖表的選擇是D3,另外還有一個將D3再包裝一次C3。C3的好處在於他的使用較D3更為簡潔,因為封裝過,所以不需要考慮底層的實做,幾乎是只要把資料倒入即可;但C3也不是沒有壞處,因為封裝過,所以要修改樣式遠比D3麻煩。
本站使用了三種不同的統計圖表:
- 圓餅圖
- 長條圖
- 橫向長條圖
本節會分別介紹三種圖表的作法。
圓餅圖
本站的圓餅圖既不是C3也不是D3,而是使用google提供的圖表工具,語法比起C3又更為簡便,因此圓餅圖使用這個。使用google chart要先設定loader和callback。
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
接著在callback內部取得資料和畫圖。首先一樣是從利用XHR取得資料,然後填入圓餅圖的資料列表中,形式是[[type1, cost1], [type2, cost2], ...],接著設定圖形的寬度和標題,最後是render在DOM上。語法很單純,不需要考慮太多,資料整理好、指定寬度、指定DOM就可以畫出來了。省去還要寫CSS、寫style的麻煩。
var list = [['type', 'cost']];
for(i in summary) {
list.push([types[i], summary[i]]);
}
var data = google.visualization.arrayToDataTable(list);
// Optional; add a title and set the width and height of the chart
var options = {'title':'月結', 'width':'100%'};
// Display the chart inside the <div> element with id="piechart"
var chart = new google.visualization.PieChart(document.getElementById('piechart'));
chart.draw(data, options);
長條圖
無論是水平的還是直的長條圖,都是使用D3來繪製的。過程中都碰到一個很麻煩的事,要如何決定寬度或高度;在記帳主頁,下方的報表區被分割為三塊,使得能夠繪製的長寬都是有限的,如何指定長寬成為繪製長條圖最重要的關卡。為了繪製長條圖,首先先要訂製長條圖使用的style。這邊展示了兩種不同的方式,#barChart是水平長條圖;而#vchart是垂直的。
.barChart{
font-family:verdana;
font-size: 13px;
}
.barChart .item {
float: left;
height: 40px;
line-height: 40px;
margin-bottom: 5px;
text-align: left;
width: 100%;
}
.barChart .bar {
background-color: DarkRed;
color: #fff;
float: left;
height: 40px;
text-align: right;
margin-right: 10px;
padding-right: 10px;
}
.vchart{
border-bottom-style:solid;
border-bottom-width:5px;
padding-left: 6px;
}
當準備好style之後就進入實際餵資料和把每一個長條畫出來的步驟。
Horizontal
水平長條圖的做法如下,首先要餵進去兩個參數,data是一個JSON格式的資料,長相是:
data = [{type1, cost1}, {type2, cost2}, ...]
還需要所有cost中的最大值,以便能夠算出水平長條的長度。假設整個要用來繪製長條圖的寬度是100%(定義在css),那麼實際用來繪製長條的寬度只保留80%,剩下的20%要用來顯示文字。而每一個長條所佔比例的公式即是:
cost/max * 80%
function draw(data, max) {
d3.select('.barChart') //選擇放在barChart這個div容器裡面
.selectAll('div') //選取".barChart"範圍內的所有的div
.data(data) //將資料加入div
.enter() //傳入資料
.append('div') //放到畫面上
.attr('class','item') //將剛剛放到畫面上的div,加上class "item"
.text(function(d){return d.type}) //加上文字描述,使用json檔案裡面的 "type" 欄位
.append('div') //加入包含資料的div,這個div是用來畫圖用的
.text(function (data) {
return data.cost; //畫圖用div加上文字描述,使用json檔案裡面的 "cost" 欄位
})
.attr('class','bar') //畫圖用div加上class "bar"
.style('width', function(d){
return (d.cost)*80/max + '%'
});
};
Vertical
至於垂直長條圖就複雜了些,因為要決定每個長條的起始位置和寬度,例如:兩筆資料時,第一個長條從0開始,寬度是50%,而文字要從25%開始,而第二根則是起始在50%,文字從75%開始。所以有三個值是需要根據資料數量做計算:
- 長條起始位置
- 長條寬度
- 文字起始位置
此外,垂直長條圖的高度有最高限制,不然若是碰到數據比例差距過大,某個長條會佔據過多版面,因此還需要計算每一根長條所能使用的高度;另一點需要注意,對於長條圖來說最底端是0,因此繪製高度是height - (d/max * height*0.8)
,此處0.8的目的與水平長條圖相同,需要有20%的高度用來放文字。至於dataset,則相對單純,因為只需要標註數字,不需要文字註記,所以不需要是JSON,只要一個數字陣列即可。
function vBarChart(dataset) {
const max = Math.max(...dataset);
const height = 300;
var chart = d3.select('.vchart')
.attr("width", '100%')
.attr('height', height)
var bar = chart.selectAll("g")
.data(dataset)
.enter().append("g")
bar.append("rect")
.attr("y",function(d,i){return height-(d/max*(height*0.8));})
.attr("x",function(d,i){
return i * 100/dataset.length+'%'; //position
})
.attr("height",function(d){
return (d*3);
})
.attr("width",100/dataset.length+'%') //width
.attr("fill","#5F4B8B")
.attr("stroke-width",2)
.attr("stroke","black");
bar.append('text')
.attr('y',function(d){return height-(d/max*(height*0.8))+21;})
.attr("x",function(d,i){
return (i * 100/dataset.length + 50/dataset.length)+'%'; //position
})
.style('fill', '#F00')
.style('font-size', '18px')
.style('font-weight', 'bold')
.style("text-anchor", 'middle')
.text(function(d){
return d;}
);
bar.append('text')
.attr('y',function(d){return height*0.1})
.attr("x",function(d,i){
return (i * 100/dataset.length + 50/dataset.length)+'%'; //position
})
.style('fill', '#000')
.style('font-size', '18px')
.style('font-weight', 'bold')
.style("text-anchor", 'middle')
.text(function(d,i){
return i+1;}
);
}
完整的程式碼在github