chart-animation について
chart-animationは、Remotionでデータに基づくアニメーショングラフや動画を作るスキルです。フレーム単位の決定的な値、読みやすい進行、複数データセットに使えるテンプレートを扱います。
主な機能
- 01フレーム駆動の決定的なアニメーション
- 02棒グラフレースと順位遷移
- 03折れ線・面グラフ・カウンターのアニメーション
- 04複数データセット向けテンプレート
- 05静止画と動画による検証
活用シーン
- 01時系列データから棒グラフレースを作成
- 02複数データセットに同じグラフデザインを適用
- 03カウンターや折れ線の表示アニメーションを制作
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ iart-ai/ data-animation-skills --skill chart-animationインストール後、Claude Code で/chart-animationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/bar-chart-race.mddata-pipeline.md詳細
データ動画
データセットを、意図を持って動くチャートに変換します。棒グラフレース、伸びていく折れ線、カウントアップする統計値などです。肝心なのは、すべてのフレームで数値をピクセルに対応させること、(不透明度だけでなく)値をイージングすること、そして視聴者が実際に読み取れるようデータの進行を調整することです。
使用する場面
- 棒グラフレース/時系列ランキング(代表的なユースケースです)。
- 折れ線/面グラフのアニメーション表示、カウンター、数字のティッカー。
- ひとつのチャートデザインを複数のデータセットに適用(テンプレート × CSV → N 本の動画)。
バグの 90% を防ぐ唯一のルール
すべての値を現在のフレームから計算します。実時間やライブラリ内部のアニメーションループは使いません。 動画の各フレームは決定論的にレンダリングされます。棒の高さを CSS トランジションや D3/GSAP/Chart.js のアニメーションから求めると、レンダラーとアニメーションの時計が一致せず、ちらつきや同期ずれが発生します。表示する値は、フレームの純粋関数として計算してください。
import { useCurrentFrame, interpolate } from "remotion";
const frame = useCurrentFrame();
const value = interpolate(frame, [0, 60], [0, 1287], { extrapolateRight: "clamp" });
Remotion では、サードパーティ製チャートのアニメーションをすべて無効にし(Chart.js では animation: false、D3 では .transition() を使わない)、useCurrentFrame() だけを時計として使います。D3 は スケールや図形(scaleLinear、scaleBand、line())には今でも優れていますが、タイマーには使いません。
データをピクセルに対応させる
チャートは、値のスケールとフレームの補間という 2 つの関数で構成されます。これらは分けて扱ってください。
| レイヤー | ツール | 役割 |
|---|---|---|
| 値のスケール | scaleLinear / scaleBand | データの単位 → px(高さ、x 座標) |
| フレーム補間 | interpolate / spring | フレーム → イージングされた進行度 0→1 |
| 表示値 | round + Intl.NumberFormat | 進行度 → 人が読む数値 |
import { scaleLinear } from "d3-scale";
const yScale = scaleLinear().domain([0, maxValue]).range([0, chartHeight]);
const barHeight = yScale(interpolate(frame, [0, 45], [0, datum.value], { extrapolateRight: "clamp" }));
値の変化をイージングする
動きをイージングするのと同じ方法で、値もイージングします。カウントアップや棒の伸長には、easeOutCubic を使うと「数値に落ち着く」ように見えます。順位の変化には spring を使うと、他の棒を追い越す棒に少し重みが加わります。
const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
const t = easeOutCubic(interpolate(frame, [start, end], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }));
const shown = from + (to - from) * t;
時計や年のカウンターのような一定の時間軸でない限り、視聴者が読む値を線形補間しないでください。見出しとなる数値が線形に動くと、機械的に感じられます。
カウンター/数字のティッカー
元の数値を補間してから、レンダリング時に書式を設定します。必須事項は 2 つです。書式設定の前に丸めること(1287.4013 のような値を避けます)、そして数字の表示幅を固定すること(数字が変わってもレイアウトが動かないようにします)。
const n = Math.round(interpolate(frame, [0, 50], [0, 1287], { extrapolateRight: "clamp" }));
const label = new Intl.NumberFormat("en-US").format(n); // "1,287"
// CSS: font-variant-numeric: tabular-nums; line-height: 1; → no width jitter, no clipping
通貨やパーセントには Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }) を使います。表形式の数字なら各桁の幅が揃い、数値が左右に揺れません。
棒グラフレース — 順位の遷移
この形式を象徴する動きです。実際のデータセットは疎です(年次/月次の行)。滑らかなレースには、データ行間の補間キーフレームと、新しい順位へスライドする棒が必要です。y 座標は補間された順位から求めるため、追い越しは飛び跳ねず、滑らかに移動します。
// frame → continuous time between sparse rows, then rank each item by its interpolated value
const t = frame / fps; // seconds
const i = Math.min(Math.floor(t), rows.length - 2);
const f = easeInOut(t - i); // 0→1 within the current row pair
const vals = items.map((it) => ({
it,
v: rows[i][it] + (rows[i + 1][it] - rows[i][it]) * f, // interpolated value
}));
vals.sort((a, b) => b.v - a.v); // rank this frame
vals.forEach((d, rank) => { d.y = rank * (barH + gap); }); // y from interpolated rank
項目ごとに安定したキーを使ってレンダリングし(React が並べ替え後も各棒を追跡できるようにします)、フレームごとの値に基づいて y と width をアニメーションさせ、年/日付ラベルも動かします。完全に動作する Remotion コンポーネントについては、references/bar-chart-race.md を参照してください。
時間に沿ったデータの進行
アニメーションが速すぎると読めません。演出にかける時間ではなく、データの各イベントに必要な時間を割り当てます。
| 拍 | 時間配分 | 理由 |
|---|---|---|
| イントロ/最初のフレーム | 1~1.5 秒静止 | 動き始める前に、軸と単位を読んでもらいます |
| データ行ごと(レース) | 0.3~0.6 秒 | 生き生きと感じられる速さで、首位の動きを追える程度に遅くします |
| 強調する場面 | +0.5~1 秒減速 | 逆転/記録/変曲点をじっくり見せます |
| 最終状態 | 2~3 秒静止 | 要点を伝え、スクリーンショットに収められる時間を確保します |
すべてのデータを一度に表示するのではなく、ひとつの系列から順に表示してください。視聴者が同時に追える動きはひとつだけです。
ラベルと注釈
文脈のない数値は雑音です。常に表示するものとして、各棒/点の値ラベル(値とともにアニメーションします)、時間表示(年/日付)、軸の単位をそれぞれ一度は表示します。コールアウトは重要な瞬間(逆転、ピーク)だけに加え、変曲点に注釈を付けたら、その後はフェードアウトさせます。
テンプレート × データ — 一括出力
コード駆動のグラフの利点は、1つのデザインで多数のデータセットに対応できることです。データセットはハードコードされた定数ではなく入力プロパティにし、ファイルごとに1回ずつレンダリングします。
// Remotion: data is a prop; same composition, different CSV → different video
export const Race = ({ data }) => { /* …reads `data`, hardcodes nothing… */ };
# render the same template for every dataset in /data
for f in data/*.json; do
name=$(basename "$f" .json)
npx remotion render Race "out/$name.mp4" --props="$f"
done
色、フォント、レイアウトは1つのテーマオブジェクトにまとめ、50本の動画でブランドの一貫性を保ち、数値だけを変更します。CSV→プロパティの解析、検証、完全な一括処理スクリプトについては、references/data-pipeline.md をご覧ください。
出力チェックリスト
- アニメーションする値はすべて
useCurrentFrame()の純粋関数にします。ライブラリのタイマーは使いません。 - 数値は丸めてフォーマットします(
Intl.NumberFormat)。数字にはtabular-numsを使います。 - レースバーには安定したキーを付けます。順位の変化は補間した順位で滑らかに移動させます。
- 軸と単位は一度だけラベル付けし、値ラベルは値とともにアニメーションさせます。
- イントロのホールド、行ごとの読みやすい表示ペース、2秒以上の最後のホールドを設定します。
- データセットは入力プロパティです。1つのテンプレートでフォルダー内のすべてのCSVをレンダリングします。
納品と検証(レンダリングした静止画 → MP4)
同梱ヘルパー(
scripts/):scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.pngで静止画を並べ、scripts/probe-mp4.sh out.mp4 [WxH] [fps]でエンコードを検証します。scripts/README.mdをご覧ください。
Remotionはフレーム決定的です。すべてのバーの高さ、カウンター値、順位は useCurrentFrame() の純粋関数なので、シーク用の仕組みがなくても、ヘッドレスで任意の正確なフレームをレンダリングできます。納品物は正確な数値を含むMP4です。そのため、静止画の確認は必須です。1ピクセルのレイアウト不具合なら許容できますが、数字の誤りは許容できません。
出力要件:
- グラフが登録されたRemotionプロジェクト(
<Composition>+ zodschema+defaultProps)。すべての動きはフレーム駆動にします(タイマー/Date.now()/Math.random()は使わず、Chart.js/D3の.transition()クロックも使いません)。 - 納品物 = レンダリング済みの
out/*.mp4(新しいデータセットでユーザーが再レンダリングできるよう、プロジェクトも含めます)。 - データに応じて再生時間が変わる場合(N行 × 1行あたりのフレーム数)は、手作業で設定せず
calculateMetadataで計算します。
検証の流れ — 静止画をレンダリング → 確認 → エンコード。 まず個別のフレームをレンダリングします(低コストで、動画エンコードは不要です)。数値とレイアウトが正しいことを確認してから、一度だけエンコードします。
# 1. Frame-exact stills at start / mid / end (PNG, headless, fast) — with the SHIPPED props
npx remotion still Race out/f-start.png --frame=0 --props='{"data":[...]}'
npx remotion still Race out/f-mid.png --frame=90 --props='{"data":[...]}'
npx remotion still Race out/f-end.png --frame=149 --props='{"data":[...]}' # last = durationInFrames - 1
# 2. Inspect each PNG — FIDELITY (axis labels, value labels, year/date, ranks, units all EXACT —
# round + tabular-nums working, no 1287.4013) AND artifacts (bar overflow, off-canvas label,
# clipped safe-area, missing font, wrong data binding / wrong column mapped).
# 3. Only after the stills check out, encode:
npx remotion render Race out/race.mp4 --props='{"data":[...]}'
npx remotion compositionsを使って各グラフのdurationInFrames/fpsを確認し、最後のフレームを選びます。- データ駆動/一括処理(中心となるケース):N個すべてのファイルを一括レンダリングする前に、代表的なデータセットを1つ、静止画で検証します。高さのスケールや列のマッピングの不具合を一度で見つけるほうが、50本のMP4で見つけるより確実です。
- README用デモGIFも無料で作成できます:
npx remotion render Race out/demo.gif --codec=gif。
完了前に確認すること:
npx remotion stillでフレーム0、中央、最後のフレームがエラーなくレンダリングされ、フォントやアセットが欠落していないこと。- すべての数値が正確で(丸め、
Intl.NumberFormat、tabular-nums)、各フレームでセーフエリア内に収まっていること。 - フレーム駆動のみであること(
Date.now()/Math.random()/ライブラリのタイマーを使わず、CIでも決定性が保たれること)。 - 納品するデータセットのプロパティが正しくレンダリングされること(
defaultPropsだけでなく、正しい列のマッピングとスケールを確認します)。 - MP4全体がエンコードされ、再生できること(任意:README用にGIFもレンダリングします)。
参考ファイル
references/bar-chart-race.md— 実行可能なRemotionの棒グラフレースコンポーネント一式。疎な行のキーフレーム補間、フレームごとの順位付け、滑らかに移動するy座標、アニメーションする値ラベル、D3スケールの軸を含みます。Vanilla Canvas版もあります。references/data-pipeline.md— CSV/JSONから型付きプロパティへの解析と検証、テーマオブジェクトのパターン、アニメーションするカウンター/線の描画レシピ、N本の動画を生成するテンプレート×データの一括レンダリングスクリプト。