Loading
  • LIGHT

  • DARK

ROUTE

ルートゼロの
アクティビティ

Chart.jsで描画が残る問題を3分で即解決!原因と対処

3

「リアルタイムでグラフを動かしていると、古い点が消えない・描画が重なる・戻ったら二重表示…」──この現象の多くは①インスタンスの重複②データ配列の未管理③タイマー/WebSocketの停止漏れが原因です。まずはこれをコピペして試してください。

<canvas id="chart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('chart').getContext('2d');
let chart = new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: 'v', data: [] }] }, options: { animation: false }});

function addPoint(x,y){
  chart.data.labels.push(x);
  chart.data.datasets[0].data.push(y);
  if(chart.data.labels.length>60){
    chart.data.labels.shift();
    chart.data.datasets[0].data.shift();
  }
  chart.update('none');
}

function cleanup(){
  if(chart){ chart.destroy(); chart = null; }
  // clearInterval / websocket close をここで必ず呼ぶ
}
</script>

問題を再現する最小サンプル(ハンズオン)

まずは最小例で現象を確認しましょう。setIntervalでデータをpushし続けると、shiftを入れていないと点が増え続け、描画が重くなったり見た目が崩れます。WebSocket再接続時にハンドラを二重登録すると、1データが2回追加されることもあります。


原因別診断チェックリスト(まずこれを確認)

  1. 同一canvasに複数のChartインスタンスが存在しないか(console.logで確認)
  2. labels/datasets[].dataの長さ管理(上限を設けているか)
  3. setInterval/requestAnimationFrame/WebSocketの解除漏れ
  4. chart.updateのモード(アニメーション有無)
  5. streamingプラグインなどの停止処理

原因別の詳しい対処法(コード+説明)

インスタンス重複→chart.destroy()を使う

Chartインスタンスを再生成する前に必ずchart.destroy()を呼び、古いリソースを解放します。再生成より破棄して再利用がおすすめです。

データが溜まる→リングバッファ戦略

配列を無限に増やさないようにif(len>MAX)shift()を入れ、可視点数を固定します。

タイマー/WSの多重登録→登録/解除パターン

ReactのuseEffectやVueのbeforeUnmountで必ずclearInterval/socket.close()を呼ぶテンプレを後述します。

アニメーション残像→chart.update(‘none’)/animation:false

頻繁に更新するならアニメーションを切り、chart.update(‘none’)で即時差分更新します。

plugin利用時の停止(例:chartjs-plugin-streaming)

プラグインに特有の停止APIがあれば使い、最後はchart.destroy()を忘れずに。


フレームワーク別の実装注意点(例:React)

React(関数コンポーネント)例:

useEffect(()=>{
  const ctx = canvasRef.current.getContext('2d');
  const chart = new Chart(ctx, {...});
  const id = setInterval(()=> addPoint(...),1000);
  return ()=>{
    clearInterval(id);
    chart.destroy();
  }
},[]);

ポイントはcleanupでタイマー停止+chart.destroy()を必ず行うこと。


パフォーマンス&運用(再発予防)

  • サンプリング:高頻度データは間引いて表示する。全点表示は不要な場合が多い。
  • メモリ監視:ChromeDevToolsのMemoryスナップショットでリークをチェック。
  • 可観測性:data.lengthや接続数をログ/メトリクス化して異常を検出。

ChatGPTを使って修正する(プロンプト+レビュー観点)

プロンプト例

>以下のコード(〜貼る)でチャートを更新していますが、画面遷移後に描画が残ります。React17+Chart.jsv3でのcleanupを追加してください。

生成コードのレビュー項目(必ずチェック)

  1. chart.destroy()の有無
  2. setInterval/requestAnimationFrameの解除
  3. WebSocketのclose
  4. dataの長さ制御(MAX)
  5. プラグインの停止

ケーススタディ(短い実務例)

  • 管理画面(setInterval):修正→shift()を追加しchart.update(‘none’)に変えるだけで安定。
  • ダッシュボード(WebSocket):再接続時に以前のonmessageを解除してから新しいハンドラを登録。
  • SPAページ遷移:unmount時にdestroyを呼ばないと戻った時に複数インスタンスが残る。

まとめ(60秒チェックリスト)

  • console.log(chart)でインスタンス数確認
  • cleanupにchart.destroy()とタイマー/wsの解除を入れる
  • data配列はmaxPointsを越えたらshift()で古い点を削除
  • 頻繁更新ではanimation:falseとchart.update(‘none’)
  • streamingpluginを使うときはpluginの停止方法を確認

参考リンク


もっとルートゼロを知りたいなら

DISCOVER MORE