JS Day 30: C3.js Chart
目標
運用 axios 把 JSON 資料載入,再使用 C3.js graph 把數據呈現出來
練習
https://codepen.io/william_k/pen/LYGXLeV
載入 C3.js & CSS Style
<!-- CSS Style Link -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.18/c3.min.css" integrity="sha512-cznfNokevSG7QPA5dZepud8taylLdvgr0lDqw/FEZIhluFsSwyvS81CMnRdrNSKwbsmc43LtRd2/WMQV+Z85AQ==" crossorigin="anonymous" />
<!-- JavaScript Link -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js" integrity="sha512-FHsFVKQ/T1KWJDGSbrUhTJyS1ph3eRrxI228ND0EGaEp6v4a/vGwPWd3Dtd/+9cI7ccofZvl/wulICEurHN1pg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.18/c3.min.js" integrity="sha512-bW79RVtvrrTS2QzmDsJeh62Nt4b/RjYlYvi2iEmkXPhzzbXMx69JT/zHgiGcL1Tk5nkLMTF6xkEUuynTkdC9PQ==" crossorigin="anonymous"></script>
元素綁定
在 C3.JS 官網裡的 Getting Started 說明的很清楚,要生成一個圖表需要提供一個元素給圖表綁定。
<div id="chart"></div>
產生圖表
#chart_donut.js
var chart = c3.generate({
data: {
// columns 是要存放排列好的陣列
// 需要把 axios 取得的 response.data 變成這樣
columns: [['data1', 30],['data2', 120],],
type : 'donut',
onclick: function (d, i) { console.log("onclick", d, i); },
onmouseover: function (d, i) { console.log("onmouseover", d, i); },
onmouseout: function (d, i) { console.log("onmouseout", d, i); }
},
donut: {
title: "Iris Petal Width"
}
});
https://c3js.org/samples/chart_donut.html
C3.js 官網裡面每一個範例下方都有內建編輯器,非常好用。可以直接把我們的資料丟進去,馬上可以看到成果 👍。
原始資料
從 JSON 取出來的資料原始面貌如下:
// Array (213)
[
{id: "4", name: "hsin-yu", process: "45%", checkpoint: {…}},
{id: "9", name: "Sesame", process: "45%", checkpoint: {…}},
.... 略
{id: "227", name: "skypassion5000", process: "0%", checkpoint: {…}}
]
// 要變成下面這個結構需要去計算每一個%出現的次數。
let chartData = [['0%', 5], ['5%', 3], ['10%', 15], ['15%', 11]];
方法其實有很多種,forEach(), map(), reduce() 都可以做到。這邊選擇使用 reduce()
,比較簡單。
reduce() 方法將一個累加器及陣列中每項元素(由左至右)傳入回呼函式,將陣列化為單一值。 — MDN web docs
// reduce() - 計算相同元素數量並以物件鍵值顯示
let chartObj = {};
function progressCollection() {
chartObj = data.reduce( (obj, item) => {
if (!obj[item.process]){ // 如果 value (例:'15%') 不存在
obj[item.process] = 0; // 新增 {'15%':0}
}
obj[item.process]++; // 把找到的+1 {'15%':1}
return obj;
}, {}); // 起始值是一個空物件 {} arg= obj
}
// chartObj output
{
0%: 59, 1.65%: 1,
5%: 12,
8.3%: 1,
10%: 9,
11.65%: 1,
15%: 19,
16.65%: 1,
20%: 21,
21.65%: 1,
23.25%: 1,
25%: 10,
28.3%: 1,
30%: 11,
35%: 4,
39.95%: 1,
40%: 18,
41.65%: 1,
43.3%: 1,
45%: 40
}
距離目標還差一小步,把物件轉成陣列,幸好 JS 原生 API 就有提供轉換
// MDN example
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj));
// [ ['foo', 'bar'], ['baz', 42] ]
// 方法二
Object.keys(chartObj).map((key) => [key, chartObj[key]]);
// 這樣就可以把 chartObj 物件轉換成陣列了
Object.entries(chartObj);
// output
[
["45%", 40],
["43.3%", 1],
["41.65%", 1],
["40%", 18],
["39.95%", 1],
["35%", 4],
["30%", 11],
["28.3%", 1],
["25%", 10],
["23.25%", 1],
["21.65%", 1],
["20%", 21],
["16.65%", 1],
["15%", 19],
["11.65%", 1],
["10%", 9],
["8.3%", 1],
["5%", 12],
["1.65%", 1],
["0%", 59]
]
互動性
setTimeout(function () {
var chart = c3.generate({
data: {
columns: pieColumns,
type: 'donut',
onclick: function (d, i) {
const selected = document.querySelectorAll('#table_body > tr > th')
selected.forEach( (item, index) => {
item.parentElement.className = '';
if (item.innerText === d.id){
item.parentElement.classList.add('table-dark');
}
})
},
// onmouseover: function (d, i) { console.log("onmouseover", d, i); },
// onmouseout: function (d, i) { console.log("onmouseout", d, i); }
},
donut: {
title: "JS學徒試煉"
}
});
}, 1000);
C3 donut chart 本身內建了一些事件,可以透過它來增強互動性。
當使用者點擊 donus chart 某個部份,下方 table row 也會選擇那幾個row
References:
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。