Chart.jsを使用して複数軸のグラフを表示する(Javascript)

## はじめに
- [こちらの投稿（【Qiita API】いろんな方法で Views、Likes、Stocksを取得（JavaScript、Google Script、Python, Vue.js）](https://qiita.com/yoshi_yast/items/a0dacc4dd33106e66752)で、Qiitaの自分の投稿について、QiitaAPIの情報を取得し、「ページビュー（View）」・「いいね（Like）」「ストック（Stock）」などをテーブル表示をさせました。

- そのときに、定期実行(1日1回)にて、データベースに毎日の情報を貯めていました(Python使用)。そのデータを使って、視覚的に見れるように、グラフ表示(複数軸)をさせようと思いました。

## やりたいこと
＜グラフ表示＞
・現在日付から過去1カ月のデータを取得する。
・取得したデータをchart.jsにてグラフ表示する。
（X軸：日付、Y軸1-棒グラフ：Qiita-View数、Y軸2-折れ線グラフ：Qiitaいいね・ストックの合計）

＜テーブル＞
・取得したデータの中で最新日付のデータをテーブルを表示する。

## 使用技術
- Javascript
- Chart.js

## デモサイト

[デモサイト](https://anotherskyjp.site/devapps/otherpg/03_qiita_graph/js_qiita_data_get_graph.html)

※これをみると、投稿がまったく見られないなんてことはなく、毎日少なからずViewがあるんだなぁと思います。（さすがQiita）

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/42363e66-a2b0-ed17-1915-a004f79bf6ed.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/4d28d5f6-bdef-3c10-00e7-6d109cd445e5.png)

## コード・ポイント
- グラフの描画部分

~~~sample.js
// グラフを描画する
var ctx = document.getElementById("canvas").getContext("2d");
window.myChart = new Chart(ctx, {
  type: 'bar',
  data: barChartData,            //グラフデータをセット
  options: complexChartOption    //データオプションをセット
});
~~~

- グラフデータのセット

~~~sample.js
// グラフデータのセット
var barChartData = {
  labels: labelData,   //ラベルデータのセット(日付)
  datasets: [
    {
      type: 'line',
      label: 'Total-Likes/Stocks',
      data: lineData,     //lineデータのセット(LIKES+STOCKSの合計)
      borderColor: "rgba(060,179,113,0.8)",
      pointBackgroundColor: "rgba(060,179,113,0.8)",
      fill: false,
      yAxisID: "y-axis-1",
    },
    {
      type: 'bar',
      label: 'Total Views',
      data: barData,       //barデータのセット(VIEWSの合計)
      borderColor: "rgba(54,164,235,0.8)",
      backgroundColor: "rgba(54,164,235,0.5)",
      yAxisID: "y-axis-2",
    },
  ],
};
~~~

- グラフオプションの設定
ticksは取得できた値に応じて、動的に算出する。

~~~sample.js
// グラフオプションの設定
var complexChartOption = {
  responsive: true,
  scales: {
  yAxes: [
    {
      id: "y-axis-1",
      type: "linear",
      position: "left",
      ticks: {
        max: setgoodsMax,     //lineデータのメモリ最大値をセット
        min: setgoodsMin,     //lineデータのメモリ最小値をセット
        stepSize: 10          //lineデータのメモリ幅をセット
      },
    },
    {
      id: "y-axis-2",
      type: "linear",
      position: "right",
      ticks: {
        max: setviewsMax,     //barデータのメモリ最大値をセット
        min: setviewsMin,     //barデータのメモリ最小値をセット
        stepSize: 1000        //barデータのメモリ幅をセット
      },
      gridLines: {
      drawOnChartArea: false,
      },
    }
  ],
};
~~~

- 全体のコード

<details><summary>全体のコード</summary>
<div>

~~~jsgraph.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Qiita Item Get Graph Display</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
  </head>

  <body onload="getData()">
    <div class="container">
      <br>
      <h3>Qiitaデータグラフ表示</h3>
      <br>
      <div class="container">
        <canvas id="canvas"></canvas>
      </div>
      <div class="container">
        <div id="result"></div>
      </div>
    </div>

    <script src="https://code.jquery.com/jquery-2.1.1.js" integrity="sha256-FA/0OOqu3gRvHOuidXnRbcmAWVcJORhz+pv3TX2+U6w=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.bundle.min.js"></script>

    <script>
      function getData(){

        var qiitadata = [];

        // 現在日付の設定
        var date = new Date();
        var yyyy = date.getFullYear();
        var mm = toDoubleDigits(date.getMonth() + 1);
        var dd = toDoubleDigits(date.getDate());
        var yyyymmdd = yyyy + mm + dd;
        var hyphendate = yyyy + "-" + mm + "-" + dd;

        // 1桁の数字を0埋めで2桁にする
        function toDoubleDigits(num){
          num += "";
          if (num.length === 1) {
            num = "0" + num;
          }
          return num;
        };

        // DB情報の取得
        var request = new XMLHttpRequest();
        request.open('GET', 'xxxxxxxxxxxxxxxxxxxx.php?setdate=' + yyyymmdd, true);
        request.responseType = 'json';

        request.onload = function () {
          qiitadata = this.response;
          drawGraph(qiitadata);                // グラフの描画
          drawTable(qiitadata, hyphendate);    // テーブルの描画
        };

        request.send();

      };

      // グラフの描画
      function drawGraph(qiitadata) {

        var nowdate;
        var sumviews = 0;
        var sumgoods = 0;
        var labelData = [];
        var lineData = [];
        var barData = [];

        // 取得データをループして、グラフ描画データにセットする
        for(var i = 0; i < qiitadata.length; i++){

          // 初回は日付のセット
          if(i == 0){
            nowdate = qiitadata[i].setdate;
          }else{
            // 日付が変わったとき
            if(nowdate != qiitadata[i].setdate){
              //データをセット
              labelData.push(nowdate);
              lineData.push(sumgoods);
              barData.push(sumviews);
              //日付のセットと合計初期化
              nowdate = qiitadata[i].setdate;
              sumgoods = 0;
              sumviews = 0;
            };
          };

          sumgoods = sumgoods + Number(qiitadata[i].likes) + Number(qiitadata[i].stocks);
          sumviews = sumviews + Number(qiitadata[i].views);

          // ループの最後の処理
          if(i == (qiitadata.length - 1)){
            labelData.push(nowdate);
            lineData.push(sumgoods);
            barData.push(sumviews);
          }

        };

        // セットされたデータからticksを算出
        var goodsMax = Math.max.apply(null, lineData);
        var setgoodsMax = goodsMax + 10;
        do {
          setgoodsMax += 1;
        } while ("00" != String(setgoodsMax).substr(-2,2));

        var goodsMin = Math.min.apply(null, lineData);
        var setgoodsMin = goodsMin - 10;
        do {
          setgoodsMin -= 1;
        } while ("00" != String(setgoodsMin).substr(-2,2));

        var viewsMax = Math.max.apply(null, barData);
        var setviewsMax = viewsMax + 1000;
        do {
          setviewsMax += 1;
        } while ("000" != String(setviewsMax).substr(-3,3));

        var viewsMin = Math.min.apply(null, barData);
        var setviewsMin = viewsMin - 1000;
        do {
          setviewsMin -= 1;
        } while ("000" != String(setviewsMin).substr(-3,3));

        // グラフデータのセット
        var barChartData = {
          labels: labelData,
          datasets: [
            {
              type: 'line',
              label: 'Total-Likes/Stocks',
              data: lineData,
              borderColor: "rgba(060,179,113,0.8)",
              pointBackgroundColor: "rgba(060,179,113,0.8)",
              fill: false,
              yAxisID: "y-axis-1",
            },
            {
              type: 'bar',
              label: 'Total Views',
              data: barData,
              borderColor: "rgba(54,164,235,0.8)",
              backgroundColor: "rgba(54,164,235,0.5)",
              yAxisID: "y-axis-2",
            },
          ],
        };

        // グラフオプションの設定
        var complexChartOption = {
          responsive: true,
          scales: {
            yAxes: [
              {
                id: "y-axis-1",
                type: "linear",
                position: "left",
                ticks: {
                  max: setgoodsMax,
                  min: setgoodsMin,
                  stepSize: 10
                },
              },
              {
                id: "y-axis-2",
                type: "linear",
                position: "right",
                ticks: {
                  max: setviewsMax,
                  min: setviewsMin,
                  stepSize: 1000
                },
                gridLines: {
                  drawOnChartArea: false,
                },
              }
            ],
          }
        };

        // グラフを描画する
        var ctx = document.getElementById("canvas").getContext("2d");
        window.myChart = new Chart(ctx, {
          type: 'bar',
          data: barChartData,
          options: complexChartOption
        });

      };

      // テーブルの描画
      function drawTable(qiitadata, hyphendate) {

        // 配列の中の最大値を取得する
        var maxno = Math.max.apply(null, qiitadata.map(function(o){return o.no;}));
        // console.log(maxno);

        var sum_view = 0;
        var sum_like = 0;
        var sum_stock = 0;

        var html = '<br><h3>Qiita記事一覧(' + hyphendate + '時点)</h3><br>' +
                  '<table class="table">' +
                  '<thead class="thead-dark">' +
                  '<tr><th scope="col">No</th><th scope="col">タイトル</th><th scope="col">VIEWS</th><th scope="col">LIKES</th><th scope="col">STOCKS</th><th scope="col">LIKE率</th><th scope="col">STOCK率</th></tr></thead><tbody>'

        // 取得データをループして、グラフ描画データにセットする
        for(var i = 0; i < qiitadata.length; i++){
          // 本日日付になったら、格納処理
          if(qiitadata[i].setdate == hyphendate){

            html +=
              '<tr>' +
              '<th scope="row">' + (maxno + 1 - Number(qiitadata[i].no)) + '</th>' +
              '<td><a href="' + qiitadata[i].url + '" target="_blank">' + qiitadata[i].title + '</a></td>' +
              '<td>' + qiitadata[i].views + '</td>' +
              '<td>' + qiitadata[i].likes + '</td>' +
              '<td>' + qiitadata[i].stocks + '</td>' +
              '<td>' + qiitadata[i].per_like + '</td>' +
              '<td>' + qiitadata[i].per_stock + '</td>' +
              '</tr>';

              sum_view += Number(qiitadata[i].views);
              sum_like += Number(qiitadata[i].likes);
              sum_stock += Number(qiitadata[i].stocks);

          };
        };

        // html に合計を設定し、書き出し
        html +=
          '<tr class="table-warning">' +
          '<th scope="row">計</th>' +
          '<td></td>' +
          '<td>' + sum_view + '</td>' +
          '<td>' + sum_like + '</td>' +
          '<td>' + sum_stock + '</td>' +
          '<td>' + '' + '</td>' +
          '<td>' + '' + '</td>' +
          '</tr>' +
          '</tbody></table>';

        var result = $("#result");
        result.empty();
        result.append(html);
      };
    </script>
  </body>
</html>
~~~

</div></details>

## まとめ
- DBなどに貯まっているデータをJavascriptでグラフ表示することができました。
- グラフの描画やグラフのオプションは、参考にさせていただいたサイトをみるととてもよくわかります（そのままのところも多々あります。感謝）
- 次は、Vue.jsで動的に動くグラフの作成を実施してみたいと思います。→2020/03月更新しました。[実践！令和 500円貯金(投資)をするといくら貯まるのか？(Vue.jsでのグラフ&テーブル表示)](https://qiita.com/yoshi_yast/items/6a057be8e454cd4ed7c5)

## 参考URL
- [Chart.jsでグラフを更新するサンプル（ボタンをクリック）(Qiita)](https://qiita.com/saka212/items/5714fa68deb44a185ec3)
- [chart.js で複数軸の複合グラフを描く(Qiita)](https://qiita.com/kd9951/items/aece80abe0bd42b3b5d3)
- [JavaScriptでオブジェクトの配列から特定の要素の最大値を得る](http://lifelog.main.jp/wordpress/?p=415)