この記事で学べること#
- Plotly.js Frames APIの基本構造
- フレームデータの作成方法
- フレームスキップによるパフォーマンス最適化(目標300フレーム)
- 静的トレースと動的トレースの使い分け
- アニメーションの基本設定
対象読者#
- D-3「Plotly.jsで3D軌道プロットを作成する」を読んだ方
- 時系列データをアニメーション表示したい方
- Plotly.js Frames APIの情報を探している方
前回の記事では、静的な3Dプロットの作成方法を解説しました。本記事では、Frames APIを使って時系列データを動的に表示するアニメーションの実装方法を学びます。
Frames APIとは?#
時系列データのアニメーション機能#
Plotly.js Frames APIは、複数のフレーム(コマ)を定義してアニメーションを作成する機能です。各フレームには、そ時点でのデータ状態を定義し、順次切り替えることで動的な可視化を実現します。
使用例#
- シミュレーション結果の時間経過表示
- 航空機の軌道追跡
- 気象データの時系列変化
- パラメータの変化を視覚的に表現
Frames APIの基本構造#
framesとは?#
framesは、各時点でのグラフの状態を定義するオブジェクトの配列です。
const frames = [
{
name: 'frame0', // フレーム名(一意な識別子)
data: [/* この時点でのtraceデータ */]
},
{
name: 'frame1',
data: [/* 次の時点でのtraceデータ */]
},
// ...
];重要なポイント:
- 各フレームは
nameプロパティで識別されます dataプロパティには、traceデータの配列を指定します- フレーム間で変化する部分のみを定義します
基本的なアニメーション実装例#
ステップ1: データの準備#
// シミュレーションデータ(時系列)
const time = [0.0, 0.1, 0.2, 0.3, 0.4];
const xs = [0, 1.5, 3.2, 5.1, 7.3];
const ys = [0, 0.5, 1.1, 1.8, 2.6];
const zs = [100, 101, 102, 103, 104];ステップ2: 静的トレースの定義#
まず、軌道全体を表示する静的なトレースを定義します。
// Trace 0: 軌道全体(静的 - 常に表示)
const trace3dStatic = {
x: xs, // 全データ
y: ys,
z: zs,
type: 'scatter3d',
mode: 'lines', // 線のみ
name: '軌道',
line: {
color: 'lightgray',
width: 2
}
};ステップ3: 動的トレースの初期状態#
次に、現在位置を示すマーカーを動的トレースとして定義します。
// Trace 1: 現在位置マーカー(動的 - フレームで更新)
const trace3dMarker = {
x: [xs[0]], // 初期位置(1点のみ)
y: [ys[0]],
z: [zs[0]],
type: 'scatter3d',
mode: 'markers', // マーカーのみ
name: '現在位置',
marker: {
size: 10,
color: 'red'
}
};ステップ4: framesの作成#
各時点でのマーカー位置を定義したframesを作成します。
// frames配列を作成
const frames = [];
for (let i = 0; i < time.length; i++) {
frames.push({
name: `frame${i}`, // ユニークな名前
data: [
// Trace 0は静的なので省略(変化しない)
// Trace 1: 現在位置マーカーのみ更新
{
x: [xs[i]], // この時点の位置
y: [ys[i]],
z: [zs[i]]
}
]
});
}重要: framesのdata配列は、更新するtraceの順番に対応します。Trace 0(静的)は省略し、Trace 1(動的)のみを更新します。
ステップ5: layout設定#
const layout = {
title: '3D軌道アニメーション',
scene: {
xaxis: { title: 'X (m)' },
yaxis: { title: 'Y (m)' },
zaxis: { title: 'Z (m)' }
},
height: 700
};ステップ6: グラフの描画#
// Plotly.newPlot()でグラフとframesを渡す
Plotly.newPlot(
'plot3d', // div要素のID
[trace3dStatic, trace3dMarker], // traceデータ配列
layout, // layout
{ frames: frames } // configオブジェクト内にframesを指定
);注意: framesは、configオブジェクトのプロパティとして渡します。
フレームスキップ最適化#
なぜ最適化が必要か?#
シミュレーションデータは数千~数万点になることがあります。すべてのデータポイントをフレームにすると、以下の問題が発生します。
- パフォーマンス低下: ブラウザが重くなる
- メモリ消費: 大量のframesオブジェクトでメモリを圧迫
- 再生速度: フレーム数が多すぎると再生が遅い
目標フレーム数の設定#
一般的に、100~300フレーム程度が適切です。滑らかな動きを維持しつつ、パフォーマンスを確保できます。
// データポイント数
const totalPoints = xs.length; // 例: 3000点
// 目標フレーム数
const targetFrames = 300;
// スキップ間隔を計算
const skipInterval = Math.max(1, Math.floor(totalPoints / targetFrames));
// 例: 3000 / 300 = 10 → 10点ごとに1フレーム作成
フレームスキップを適用#
const frames = [];
for (let i = 0; i < totalPoints; i += skipInterval) {
frames.push({
name: `frame${i}`,
data: [
{
x: [xs[i]],
y: [ys[i]],
z: [zs[i]]
}
]
});
}結果: 3000点 → 300フレーム(10点ごとに1フレーム)
静的トレースと動的トレースの使い分け#
静的トレース(Static Traces)#
軌道全体を常に表示する場合、静的トレースを使用します。
// 軌道全体(静的)
const trace3dStatic = {
x: xs, // 全データ
y: ys,
z: zs,
type: 'scatter3d',
mode: 'lines',
name: '軌道'
};framesでは更新しないため、data配列に含めません。
動的トレース(Animated Traces)#
現在位置マーカーのように、フレームごとに変化する場合、動的トレースを使用します。
// 現在位置マーカー(動的)
const trace3dMarker = {
x: [xs[0]], // 初期位置
y: [ys[0]],
z: [zs[0]],
type: 'scatter3d',
mode: 'markers',
name: '現在位置'
};framesで毎回更新します。
アニメーションの再生方法#
自動再生#
Plotly.newPlot()実行後、自動的にアニメーションを再生するには、Plotly.animate()を呼び出します。
// グラフ描画
Plotly.newPlot('plot3d', [trace3dStatic, trace3dMarker], layout, { frames: frames });
// 自動再生開始
Plotly.animate('plot3d', null, {
frame: {
duration: 50 // 各フレームの表示時間(ミリ秒)
},
transition: {
duration: 0 // フレーム間の遷移時間(0で即切り替え)
},
mode: 'afterall' // 全フレームを順次再生
});設定項目:
duration: 各フレームの表示時間(ms)transition.duration: フレーム間の遷移時間(ms)mode: 再生モード('immediate','next','afterall')
複数トレースのアニメーション#
複数の動的トレースを更新#
複数のトレースをアニメーションさせる場合、framesのdata配列に対応する順番でトレースデータを指定します。
// Trace 0: 軌道(静的)
// Trace 1: 現在位置マーカー(動的)
// Trace 2: 速度ベクトル(動的)
const frames = [];
for (let i = 0; i < totalPoints; i += skipInterval) {
frames.push({
name: `frame${i}`,
data: [
// Trace 0(静的)は省略
// Trace 1: 現在位置
{
x: [xs[i]],
y: [ys[i]],
z: [zs[i]]
},
// Trace 2: 速度ベクトル
{
x: [xs[i], xs[i] + vx[i]],
y: [ys[i], ys[i] + vy[i]],
z: [zs[i], zs[i] + vz[i]]
}
]
});
}よくある間違いと対処法#
間違い1: すべてのtraceをframesに含める#
// ❌ 悪い例: 静的トレースも毎回更新
frames.push({
name: `frame${i}`,
data: [
{ x: xs, y: ys, z: zs }, // 軌道全体(不要な更新)
{ x: [xs[i]], y: [ys[i]], z: [zs[i]] } // 現在位置
]
});問題: 静的トレースを毎回更新するため、パフォーマンスが低下します。
対策: 静的トレースはframesに含めず、動的トレースのみ更新します。
間違い2: フレーム数が多すぎる#
// ❌ 悪い例: 全データポイントをフレーム化(3000フレーム)
for (let i = 0; i < xs.length; i++) {
frames.push({...});
}問題: ブラウザが重くなり、メモリを大量に消費します。
対策: フレームスキップで目標フレーム数(100~300)に抑えます。
まとめ#
本記事では、Plotly.js Frames APIの基本的な使い方を解説しました。
重要なポイント:
- framesは各時点の状態を定義するオブジェクトの配列
- 静的トレースと動的トレースを使い分ける
- フレームスキップで目標300フレーム程度に最適化
Plotly.newPlot()のconfig引数でframesを渡すPlotly.animate()で再生開始
次のステップとして、updatemenusとslidersを使ったアニメーション再生コントロール(再生/一時停止ボタン、シークバー)の実装に挑戦してみましょう。
参照資料#
本記事の執筆にあたり、以下の資料を参照しました [@plotly_js_docs_animations_2025; @plotly_js_github_frames_examples_2025]。