やりたいこと
- 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();
}
}]
}
若干ラグはあるものの、それっぽく出来ました。
ホバーした際の挙動はこちら。
ゆ、許してくれますか?
ソースの全体はココに置いておきます。
出来てないこと
完成したかのようで、出来てない部分があります。
実は横幅を固定していたため、レスポンシブな対応は出来ていません。
overwriteCanvas = function () {
// ... 中略
if (width * 180 < i && i < width * 305) {
このif文はCanvasのサイズが固定であることを前提にしていて、
本来は画面resizeイベントに応じて計算しないとイカン部分でした。
計算方法がいまいち分からんので諦めてます。ごめんなさい。