生活の跡

個人的な備忘録

Chart.jsの散布図で、データを一つずつプロットする

やりたいこと

こういう感じで、一定時間間隔でデータを一つずつプロットしたい。

f:id:ishii-akihiro:20200318133658g:plain

横軸が時間のデータではアニメーションにする必要性を感じませんが、媒介変数表示の場合などは時間に沿ってプロットすることで理解しやすくなる気がします。

ライブラリ

・Chart.js 2.7.3

方針

  • 時間 \(t\) を変数とするループ処理の中で、ある時間 \(t_i\) におけるデータ \(x_i\) を作成します。
  • ループするごとにデータを再描画します。
  • 再描画の間隔は0.1秒とします。

3つ目の方針にあるように再描画の間隔を設定したいのですが、javaScriptには処理を一時停止する関数が用意されていないようです。そこで、setTimeout関数を応用してなんとかします。詳しくは本記事の最下部に貼ったリンク先を参照してください。

手順

  1. 一旦、データが空の状態で描画処理を行う
  2. ループ処理内で、一定時間(0.1秒)おきにデータ生成&再描画処理を行う

コード

HTML(body部のみ記載)

  <div class="canvas-container">
    <canvas id="canvas_1"></canvas>
  </div>

javaScript(jQuery)

$(function(){
  // データを格納する配列の用意
  data = [];

  // パラメータ設定
  var dt = 0.1; // 計算する時間の間隔
  var N = 20;   // ループ回数

  // 1. 一旦、データが空の状態で描画処理を行う
  var ctx = $('#canvas_1')[0].getContext('2d');
  var chart = new Chart(ctx, {
    type: 'scatter',
    data: {
      datasets: [
        {
          data: data,  //ここではデータは空
        }
      ]
    },
    options: {
      scales: {
        xAxes: [
          {
            ticks: {
              min: 0,
              max: 2.0,
            }
          }
        ],
        yAxes: [
          {
            ticks: {
              min: 0,
              max: 2.0,
            }
          }
        ]
      },
      animation: false,
    },
  });

  // ループ処理に相当する関数(手順2の処理を準備)
  function doSomethingLoop(maxCount, i) {
    if (i <= maxCount) {
      // 時間をdtずつ進めて、x(t)を計算
      t = i * dt;
      x = (1/2) * (t**2);
      
      // 値を配列に格納
      data.push({x: t, y: x});

      // 再描画
      chart.data.datasets[0].data = data;
      chart.update();

      setTimeout(function(){doSomethingLoop(maxCount, ++i)}, 100);  // 0.1s = 100ms 間隔で処理
    }
  }

  // 2. 一定時間(0.1秒)おきにデータ生成&再描画処理を行う
  doSomethingLoop(N, 0);
});

留意事項

データ数(再描画数)が多いと動作ももっさりすると思います。

おわりに

ひとまず目的の処理を実装できました。 ただ、setTimeout関数の使い方はちゃんと理解しているわけではないので、詳しく知りたい方は次のリンク先を参照してください(本記事を書くにあたり参考にさせていただきました)。

qiita.com