JS Day 30: C3.js Chart

有 N 人看过

目標

運用 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 官網裡面每一個範例下方都有內建編輯器,非常好用。可以直接把我們的資料丟進去,馬上可以看到成果 👍。

JS%20Day%2030%20C3%20js%20Chart%20596ae2ec69c04c08b18a60b77394d6dd/Screen_Shot_2020-07-18_at_11.04.51_PM.png

原始資料

從 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

JS%20Day%2030%20C3%20js%20Chart%20596ae2ec69c04c08b18a60b77394d6dd/Screen_Shot_2020-07-18_at_11.18.06_PM.png

// 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 国际许可协议 进行许可。