LoginSignup
13
1

More than 3 years have passed since last update.

ChartJSの線グラフで、閾値以上になったら色を変えたい

Last updated at Posted at 2020-11-30

やりたいこと

  • ChartJSを使って線グラフを書きたい
  • ある一定の値を超えていたら線の色を変えたい

こんな要望をちらほら聞き、どうにか実現出来ないかなぁ。
と思ったので、ゴリ押しでやってみました。

データセットを入れるタイミングで、
「閾値未満は青色」「閾値以上は赤」というデータを分ける方法ももちろんあるのですが
「ある値以上の線」だけを変えたい、という細かな部分に対応出来なかったので、試行錯誤してみました。

方針

  • 描画後のイベントを取得
  • 対象グラフのCanvasをピクセル毎に取得
  • ピクセルの位置と色を判定して、書き換える

中身

Canvasの線グラフ部分を上書きするような関数を用意しておきます。

const overwriteCanvas = function () {
  console.log("overwriteCanvas");
  const targetCanvas = document.getElementById("targetCanvas");
  const targetCanvasContext = targetCanvas.getContext('2d');
  const {width, height} = targetCanvas;
  const mainImgData = targetCanvasContext.getImageData(0, 0, width, height);
  const mainData = mainImgData.data;
  //Canvasの各ピクセル毎をみて、色を塗り替える。
  for (let i = 0, len = width * height; i < len; i++) {
    // 高さ180pxから305pxまでの範囲で色を変える
    if (width * 180 < i && i < width * 305) {
      const p = i * 4;
      // RGB(54, 162, 235)を RGB(255, 99, 132) (赤) に変える。
      if (mainData[p] === 54 && mainData[p + 1] === 162 && mainData[p + 2] === 235) {
        mainData[p] = 255;
        mainData[p + 1] = 99;
        mainData[p + 2] = 132;
      }
    }
  }
  targetCanvasContext.putImageData(mainImgData, 0, 0);
}

さてさて、こんな関数を用意したものの「いつ動かすか」が問題です。。。

window.addEventListener('load', () => {
  const ctx = document.getElementById('targetCanvas').getContext('2d');
  window.myLine = new Chart(ctx, config);
  overoverwriteCanvas();
});

のように動かしても、ChartJS側の描画タイミングが取れず、用意した関数のほうが早く終わってしまいます。

Promiseでやるか → ダメでした。
青から赤に変化させられるが、
ホバーするとグラフが更新され、色が戻ってしまう現象が起きました。

描画後のタイミングが取れない限りダメってことか...。

そんな中、更新らへんのソースを読んでると...

afterUpdateっていう、怪しいやーつが、ナンカあるぞ。。。!
さらに読んでいくと、afterRenderというまさにソレなモノまでありますな。。。

確かに、ドキュメントを読むと...書いてありますね。
でもPluginって、推測出来んすわ。(ポンコツ)

ってことで!

ChartJSのconfigを設定します

const config = {
  type: 'line',
  data: {}, // 割愛
  options: {
    responsive: false,
    title: {},    // 割愛
    tooltips: {}, // 割愛
    hover: {},    // 割愛
    scales: {}    // 割愛
  },
  plugins: [{
    // 更新後のイベント
    afterUpdate: function () {
      console.log('afterUpdate');
      overwriteCanvas();
    },
    // 描画後のイベント
    afterRender: function() {
      console.log('afterRender');
      overwriteCanvas();
    }
  }]
}

ezgif.com-gif-maker.gif

若干ラグはあるものの、それっぽく出来ました。
ホバーした際の挙動はこちら。
ezgif.com-gif-maker (1).gif

ゆ、許してくれますか?

ソースの全体はココに置いておきます。

出来てないこと

完成したかのようで、出来てない部分があります。
実は横幅を固定していたため、レスポンシブな対応は出来ていません。

  overwriteCanvas = function () {
      // ... 中略
      if (width * 180 < i && i < width * 305) {

このif文はCanvasのサイズが固定であることを前提にしていて、
本来は画面resizeイベントに応じて計算しないとイカン部分でした。

計算方法がいまいち分からんので諦めてます。ごめんなさい。

参考文献

[図解] HTML5 Canvasあれこれ - レイヤー, マスク(ImageData), 全画面でのハマりポイント

13
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
1