data-story-charts について
Data Story Chartsは、データを短い解説動画にするためのチャートアニメーションスキルです。データを紹介する制作者向けに、数値、チャート、注釈をストーリー順に表示し、出典と正確性を重視します。
主な機能
- 01折れ線、棒、積み上げ、カウンターチャート
- 02チャートと同期する数値カウンター
- 03ストーリー順のデータ表示
- 04重要なデータ点への注釈
- 05滑らかな軸スケール変更
活用シーン
- 01データの変化を短い動画にする
- 02傾向のピークや転換点を強調する
- 03注釈で重要な数値を説明する
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ phantomtonyt/ motion-design-skills --skill data-story-chartsインストール後、Claude Code で/data-story-chartsを入力して読み込まれたか確認します。
詳細
データストーリーチャート
ストーリーの順に組み立て、順番に明かしていく1つのチャートです。主役は数字です。上部に大きなカウンターを配置し、それに合わせて動く折れ線や棒、変化の理由を伝える注釈ピンを添えます。解説報道のような、明快で落ち着いた印象を目指し、ドラマチックな場面は1〜2回にとどめます。
オプション
| オプション | デフォルト | メモ |
|---|---|---|
| 形式 | 縦型 1080×1920 | または横型 1920×1080 |
| 長さ | 15–20 s | データの展開1つにつき約3–5 s |
| チャート | 折れ線 | line(時間による変化)、bars(比較)、stacked(合計の内訳)、counter(大きな数字を1つ) |
| トーン | エディトリアル | editorial(デフォルト、明るい紙面)、dark(ネイビー、光るアクセント)、punchy(テンポが速く、重要な場面で色が切り替わる) |
ブリーフ(計画の前に回答)
- 視聴者に覚えてほしい数字または変化は何ですか?
- データは何ですか(貼り付けるか、ファイルを指定してください)。単位、期間、出典も記載してください。
- 意外な点は何ですか。急増、急落、逆転、隠れたコストなどですか?
- 注釈ピンを付けるべき場面はどの2〜4か所ですか?
- 締めくくりの一文は何ですか?
ユーザーが提示した実際の数値か、データから計算した数値だけを使用してください。データを捏造してはいけません。推定値の場合は、画面上でその旨を明記してください。
読み取りシート(ビートリストと一緒に作成)
各ビートについて、視聴者が理解しなければならないことを1つ(「ロボットは飛べない」「キャッシュがヒットしなかった」など)と、それを読み取れる状態で表示する時間を記載してください。一度に読み取る内容は1つだけにします。テキスト表示時間の下限:ラベルは最低0.8 s、文は画面に完全に表示されてから単語あたり約0.3 s。表示時間に収まらない場合は、速度を上げずに言葉を削ってください。
モーションの原則
- モーションには因果関係を持たせます。画面上の理由(クリック、落下、パケットの到着など)に応じて動かし、次の動きはその結果としてつなげます。
- 動画全体を通して、主な移動方向を1つ選び、それを維持します。それ以外の方向は意味を持たせて使います。成功や結論には上方向、「全体像はこちら」にはズームアウトを使います。
- シーンの退出を次のシーンの登場につなげます。同じ軸、同じ方向、似た速度を使います。クロスフェードではなく、変化をまたいで1つのオブジェクトを引き継ぎます。
- 図、チャート、テキストに目的のない埋め草の動き(浮遊、呼吸、揺れ)を加えません。動かす必要がなければ静止させます。意図的なカメラの動きは1つで十分です。
- イージング:登場時は滑らかなイーズアウト(
power2.outからpower4.out)、退出時はより素早いイーズイン(登場時間の約75%)。登場は最大0.8 s。グループ全体のスタッガーは最大0.5 s。オーバーシュート(back.out)は小さなポップに限り、弾性やバウンスは使いません。 - 重要な場面では、一度に動かすものは1つにします。視線が追えるのは1つだけです。
- 見せ場の直前に、0.3〜0.75 sの静止時間を設けます。
スタイルルール(常に適用)
- シーンごとのチャートタイプは1つ、焦点を当てる系列も1つにします。それ以外の系列はグレーにします。
- チャートは時間の経過またはストーリーに沿って表示します。線は左から右へ描き、棒は順番に伸ばします。話題にする前に表示してはいけません。
- 大きなカウンターには現在の値を表示し、チャートに合わせて数値を更新します(等幅数字)。
- データが軸の範囲を超えたら、画面上で軸を再スケーリングします(目盛りとラベルをイーズ付きで変化させます)。この再スケーリングは、通常、動画の最も見せ場になる瞬間です。
- ピンを使って、重要な2〜4か所に短いラベルを付けます。
- すべてのラベルでコントラスト比3:1を確保します。グリッドと目盛りラベルには、薄いグレーではなく落ち着いたダークグレーを使います。
デフォルトと上書き
優先順位:ユーザーのブランドまたは仕様、明示的なリクエスト、これらのデフォルトの順です。
- エディトリアル:紙
#f7f4ee、インク#16213a、グリッド#e3ded4、目盛りラベル#5b6270、アクセント#e4572e、プラス#047857、警告#a16207、マイナス#c2410c。 - ダーク:ステージ
#0f1724、インク#f2f5f9、グリッド#1f2b3d、ラベル#9fb0c8、アクセント#38d6e0。 - フォント:Inter(カウンターは900、140–170 px、キャプションは800、56–64 px、ラベルは600、26–30 px)。
セットアップ(1回)
node -v && ffmpeg -version
npx hyperframes init my-video --non-interactive --resolution portrait && cd my-video # landscape for 16:9
種類
| 必要なもの | HyperFrames の SVG + GSAP(デフォルト) | Remotion | HyperFrames 内の D3 |
|---|---|---|---|
| 線、棒、カウンター、ピン | はい | はい | はい |
| 数千個の点または地図投影 | 負荷が高い | 負荷が高い | はい(時刻 t から描画) |
| Node と FFmpeg だけで動作 | はい | はい | はい |
レイアウト
- 9:16: キッカーラベルを y 285、カウンターを y 350(150 px)、グラフ領域を x 120–960、y 520–1000 に配置し、軸ラベルをその下に、キャプションを y 1110–1250 に配置します。
- 16:9: カウンターを左上、グラフを x 200–1720、y 260–880 に配置し、キャプションを左下またはグラフの右側に配置します。
- ピンは破線の軸線を付けてデータポイントの上に配置します。カウンターやキャプションに重ならないようにします。
アニメーション方法(シークセーフ)
データを関数または配列として記述し、1 つのクロック値 t からグラフ全体を描画します。1 つのトゥイーンでクロックを動かし、その onUpdate で再描画します。
const clock = { t: 0 };
tl.to(clock, { t: DURATION, duration: DURATION, ease: "none", onUpdate: () => draw(clock.t) }, 0);
draw(0);
draw(t) は t までの折れ線パスを再構築し、マーカーとカウンターを動かします。また、現在の軸の最大値(それ自体も t の関数です)に基づいてピンと軸ラベルを配置します。すべてのピクセルが t から決まるため、HyperFrames がシークしても、どのフレームも常に同じようにレンダリングされます。
| 動き | 方法 |
|---|---|
| 線の表示 | t までのサンプルからパスを構築します(データ 1 秒あたり約 30 個)。 |
| 線の下の塗りつぶし | 同じパスをベースラインまで閉じ、アクセントカラーを不透明度 12% で適用します。 |
| 棒の伸長 | 高さを 0 からトゥイーンし、0.08 s ずつずらして開始します。power3.out を使用します。 |
| カウンター | 現在の値をテキストで表示します。10 未満は小数第 1 位まで、それ以上は整数で表示します。 |
| 軸の再スケーリング | yMax(t) を 1~1.5 s かけて古い最大値から新しい最大値へ滑らかに変化させ、目盛りラベルも更新します。 |
| ピンのポップ | ピンの時刻に back.out(2.5) を使って、スケールを 0.5 から 1 にします。 |
| 強調 | 重要な数値の表示中、カウンターを 1.12 倍に拡大してから元に戻します。 |
目を引く仕上がりにする
- 最初のフレームから、カウンター、軸、問いが表示されています。
- 驚きを与える場面では、0.5 秒以内に再スケーリング、ピンの表示、キャプションの変更をすべて行います。
- 小さなマーカー(点、アイコン、キャラクター)を線の先端に沿って動かし、視線を誘導します。
- 面の塗りつぶしと先頭の点に加える柔らかなアクセントの光で、画面を散らかさずに奥行きを出します。
- キャプションは短い文にし、数値をアクセントカラーで表示します(「その後:120 m.」)。
- 静止したどのフレームにも、それ単独で事実が示されています。
スターター
<div id="root" data-composition-id="main" data-start="0" data-duration="10" data-width="1080" data-height="1920" style="background:#f7f4ee;font-family:Inter,sans-serif">
<svg viewBox="0 0 1080 1920" width="1080" height="1920" style="position:absolute;inset:0">
<g id="grid" font-size="26" fill="#5b6270"></g>
<line x1="120" y1="1000" x2="960" y2="1000" stroke="#16213a" stroke-width="4"/>
<path id="area" fill="#e4572e" opacity=".12"/><path id="line" fill="none" stroke="#e4572e" stroke-width="9" stroke-linejoin="round" stroke-linecap="round"/>
<circle id="dot" r="14" fill="#e4572e"/>
</svg>
<div style="position:absolute;left:60px;top:350px;font:900 150px/1 Inter,sans-serif;color:#16213a;font-variant-numeric:tabular-nums"><span id="num">0</span></div>
</div>
<script>
const D = 10, X0 = 120, X1 = 960, Y0 = 1000, Y1 = 520;
const data = (t) => 2 * t + (t > 6 ? 40 * ((t - 6) / 4) ** 2 : 0); // replace with the user's data
const yMax = (t) => 20 + 60 * Math.min(1, Math.max(0, (t - 6.5) / 1.2)); // axis rescales at the surprise
const px = (t) => X0 + (X1 - X0) * (t / D), py = (v, m) => Y0 - (Y0 - Y1) * (v / m);
const NS = "http://www.w3.org/2000/svg", grid = document.getElementById("grid");
const ticks = [0, 0.5, 1].map((f) => { const l = document.createElementNS(NS, "line"), x = document.createElementNS(NS, "text");
l.setAttribute("x1", X0); l.setAttribute("x2", X1); l.setAttribute("stroke", "#e3ded4"); x.setAttribute("x", X1); x.setAttribute("text-anchor", "end"); grid.append(l, x); return { f, l, x }; });
function draw(t) {
const m = yMax(t); let d = "";
ticks.forEach(({ f, l, x }) => { const y = Y0 - (Y0 - Y1) * f; l.setAttribute("y1", y); l.setAttribute("y2", y); x.setAttribute("y", y - 10); x.textContent = Math.round(m * f); });
const n = Math.max(2, Math.round(t * 30));
for (let i = 0; i <= n; i++) { const s = (t * i) / n; d += (i ? "L" : "M") + px(s).toFixed(1) + " " + py(data(s), m).toFixed(1); }
document.getElementById("line").setAttribute("d", d);
document.getElementById("area").setAttribute("d", d + "L" + px(t) + " " + Y0 + "L" + X0 + " " + Y0 + "Z");
const dot = document.getElementById("dot"); dot.setAttribute("cx", px(t)); dot.setAttribute("cy", py(data(t), m));
document.getElementById("num").textContent = Math.round(data(t));
}
const tl = gsap.timeline({ paused: true }), clock = { t: 0 };
tl.to(clock, { t: D, duration: D, ease: "none", onUpdate: () => draw(clock.t) }, 0);
draw(0);
window.__timelines["main"] = tl;
</script>
GSAP(https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js)、Google Fonts の Inter フォント、および html,body{margin:0;width:1080px;height:1920px;overflow:hidden} を含む <head> を追加します。同じタイムラインにキッカー、キャプション、ピンを追加します。
公開するパラメーター
format · duration · chart · tone · data(配列または関数) · units · pins(時刻、ラベル、色) · captions(時刻、テキスト) · rescaleAt。
避けること
- 画面上の数値はすべて、ユーザーのデータまたは出典を引用したものにします。コンポジションの横に、小さな事実リスト(数値、出典)を置きます。説明用の数値は、画面上で説明用と明記します。
- 作り出した数値や「説明用」の数値を、実際の数値として表示すること。
- 複数の系列を同時に強調することや、虹色のパレット。
- 薄いグレーのラベル(コントラストチェックに通らず、スマートフォンでは読めません)。
- グラフを見づらくする要素:3D の棒、濃いグリッド線、直接ラベルで済む場合の凡例。
- 軸をイージングなしで急に動かすこと。変化が視聴者に伝わるよう、滑らかに再スケーリングします。
- ピンやキャプションで、線の重要なポイントを覆うこと。
失敗しやすいポイント
| 状況 | 原因 | 対処法 |
|---|---|---|
| レンダリング時に線が飛んだりちらついたりする | 壁時計の時刻やフレームごとの乱数を使って描画している | onUpdate ではクロック t のみを使って描画します |
| ピンのラベルがピルからはみ出す | ピルの幅を小さく見積もっている | 28 px では幅を約 36 + 文字数 × 17(すべて大文字の場合は × 23)にするか、フォントの読み込み後に測定します |
| キッカーとカウンターが重なる | どちらも近すぎる位置に配置されている | キッカーのベースラインとカウンターの上端の間を 30 px 以上空けます |
| 驚きの演出が平坦に見える | スケール変更、ピン表示、キャプション変更がない | 3つすべてを 0.5 秒以内に重ねて実行します |
| カウンターの文字が揺れる | プロポーショナル数字を使っている | font-variant-numeric: tabular-nums |
| 目盛りのコントラストチェックに失敗する | ラベルの色が明るすぎる | 明るい背景では目盛りを #5b6270、暗い背景では #9fb0c8 にします |
検証
npx hyperframes check
npx hyperframes snapshot --at <start, each pin, mid-rescale, end>
npx hyperframes render -o renders/<name>.mp4
- 画面上の数値が、それぞれの時点のデータと一致している
- スケール変更が滑らかで、その間もラベルが読みやすい
- ピンやキャプションが、線の重要なポイントに決して重ならない
- すべてのテキストがセーフエリア内にあり、コントラストチェックに合格している
- 最終フレームで要点を伝えている
最終レンダリング前の自己評価
スナップショットシートを、引き(最初の1秒)、スマートフォン幅での読みやすさ、動きの質、構図、正確さ、「ミュートテスト」(音なしで視聴者が一文で要約できるか)の各項目について、1〜10点で評価します。7点以下の項目だけ修正し、再度スナップショットを作成して、すべてが8点以上になるまで繰り返します。可能であれば、自分の作品を評価するのではなく、別の読み取り専用の工程で採点してもらいます。
| 避けること | 代わりにすること |
|---|---|
| 情報量の多い2つのレイアウトをクロスフェードする | 古いコンテンツを退出させてから新しいコンテンツを表示するか、1つのオブジェクトを引き継ぎます |
| 複数の要素を同時に動かす | タイミングをずらし、その瞬間に注目させる要素を1つにします |
| 間を埋めるために浮遊や揺れを加える | 静止させるか、キャラクターに動作させます |
| 「勢い」を出すためにパーティクル、レインボーグラデーション、3D フリップを加える | より強いイベント、たとえば表示、衝突、カメラ移動を加えます |
| 時間内に収めるためにテキストを急いで表示する | 言葉を削ります |
納品
- ポスター:最終チャートの静止フレームをフレーム 0 に埋め込みます:
ffmpeg -ss <t> -i renders/<name>.mp4 -frames:v 1 poster.jpg、次にffmpeg -i renders/<name>.mp4 -i poster.jpg -filter_complex "[0:v][1:v]overlay=enable='eq(n,0)'" -c:a copy renders/<name>-final.mp4を実行します。 - 重要な数値とデータソースを記載したキャプション。
- データの展開と、修正時に変更する数値やピンをユーザーに伝えます。
参考資料
- @kimmonismus、「A Short History of AI」。Remotion で制作された、マイルストーンカウンター付きのタイムライン:https://x.com/kimmonismus/status/2102844654169575547
- @VincentWei93、Anthropic の成長をデータビジュアライゼーションのモーショングラフィックスで表現した作品。数値に合わせた効果音付き:https://x.com/VincentWei93/status/2103381720410333314
- @sonnylazuardi、インドネシアの81年間の歴史を42,000点のドットマップで表現した作品:https://x.com/sonnylazuardi/status/2103511590884815282
- @aakashgupta、積み上げ棒グラフとカウンターで報酬オファーの内訳を示した作品:https://x.com/aakashgupta/status/2102998741359988832
- HyperFrames のモーションドクトリンと、顔出しなしの解説動画のスキル、JohnHeibel/ClaudeAnimationBase のアニメーションガイド、howseen-ai/claude-motion-design の批評ループ、latent-spaces/brag から着想を得ています(コピーではありません)。