「リアルタイムでグラフを動かしていると、古い点が消えない・描画が重なる・戻ったら二重表示…」──この現象の多くは①インスタンスの重複②データ配列の未管理③タイマー/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回追加されることもあります。
原因別診断チェックリスト(まずこれを確認)
- 同一canvasに複数のChartインスタンスが存在しないか(console.logで確認)
- labels/datasets[].dataの長さ管理(上限を設けているか)
- setInterval/requestAnimationFrame/WebSocketの解除漏れ
- chart.updateのモード(アニメーション有無)
- 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を追加してください。
生成コードのレビュー項目(必ずチェック):
- chart.destroy()の有無
- setInterval/requestAnimationFrameの解除
- WebSocketのclose
- dataの長さ制御(MAX)
- プラグインの停止
ケーススタディ(短い実務例)
- 管理画面(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の停止方法を確認