mcpField
C
chart-animation

chart-animation

github.com/iart-aiクリエイティブ・メディア151.4k 回インストール18 週間前に更新
リポジトリ

chart-animation について

chart-animationは、Remotionでデータに基づくアニメーショングラフや動画を作るスキルです。フレーム単位の決定的な値、読みやすい進行、複数データセットに使えるテンプレートを扱います。

主な機能

  1. 01フレーム駆動の決定的なアニメーション
  2. 02棒グラフレースと順位遷移
  3. 03折れ線・面グラフ・カウンターのアニメーション
  4. 04複数データセット向けテンプレート
  5. 05静止画と動画による検証

活用シーン

  1. 01時系列データから棒グラフレースを作成
  2. 02複数データセットに同じグラフデザインを適用
  3. 03カウンターや折れ線の表示アニメーションを制作

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/iart-ai/data-animation-skills --skill chart-animation

インストール後、Claude Code で/chart-animationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
references/
bar-chart-race.md
data-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> + zod schema + 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。

完了前に確認すること:

  1. npx remotion still でフレーム0、中央、最後のフレームがエラーなくレンダリングされ、フォントやアセットが欠落していないこと。
  2. すべての数値が正確で(丸め、Intl.NumberFormat、tabular-nums)、各フレームでセーフエリア内に収まっていること。
  3. フレーム駆動のみであること(Date.now()/Math.random()/ライブラリのタイマーを使わず、CIでも決定性が保たれること)。
  4. 納品するデータセットのプロパティが正しくレンダリングされること(defaultProps だけでなく、正しい列のマッピングとスケールを確認します)。
  5. MP4全体がエンコードされ、再生できること(任意:README用にGIFもレンダリングします)。

参考ファイル

  • references/bar-chart-race.md — 実行可能なRemotionの棒グラフレースコンポーネント一式。疎な行のキーフレーム補間、フレームごとの順位付け、滑らかに移動するy座標、アニメーションする値ラベル、D3スケールの軸を含みます。Vanilla Canvas版もあります。
  • references/data-pipeline.md — CSV/JSONから型付きプロパティへの解析と検証、テーマオブジェクトのパターン、アニメーションするカウンター/線の描画レシピ、N本の動画を生成するテンプレート×データの一括レンダリングスクリプト。

コメント