1仕組みと動作原理
バグの90%を防ぐ鉄則:すべての表示値は現在のフレームから導出すること。実世界時間やライブラリ内部のアニメーションループは使用禁止。
**Drive every value from the current frame — never from wall-clock time or a library's internal animation loop.**
チャートを値域マッピング、フレーム補間、表示値フォーマットという3つの分離可能な関数に分割する。
| Value scale | `scaleLinear` / `scaleBand` | data units → px (height, x-position) |
棒グラフレースの2大難所:スパースデータの補間と、順位のスムーズなスライド(y座標は補間された順位から算出)。
Real datasets are sparse (yearly/monthly rows); a smooth race needs **interpolated keyframes between data rows** plus bars that slide to new ranks.
レンダリング前にデータ検証が必須:警告なしのNaNは高さ0のバーとなり、3分後にようやく発覚する。
Treat the dataset as the single input. Parse, validate, and *fail loud* before rendering — a silent `NaN` becomes a bar of height 0 in the final MP4, which you only notice after a 3-minute render.
クリーニングルールのチェックリスト化:数値の型強制検証、欠損値の明示的補完、時系列昇順、表示件数の上限(約12本)。
- **Cap item count.** Beyond ~12 bars a race is unreadable; keep top-N per frame and drop the rest, or pre-aggregate the long tail into "Other".
カウンターの精度:数値を補間 → 四捨五入 → formatし、tabular-numsで文字幅を固定する。
Interpolate the underlying number, then format on render. Two musts: **round before formatting** (no `1287.4013`), and **fix the digit box** so the layout doesn't jump as digits change.
ペーシング予算は「データイベントごと」に算出:1行あたり0.3〜0.6秒、重要ポイントで減速、最終フレームで2〜3秒静止。
| Per data row (race) | 0.3–0.6s | fast enough to feel alive, slow enough to track the leader |
アノテーションは重要な瞬間(交差ポイント、ピーク)にのみ表示し、時間枠を用いてフェードイン・フェードアウトさせる。
const eventFrame = fps * 6.2; // when the crossover happens
検証は省略不可:成果物は正確な数値を担うため、1ピクセルのレイアウトバグは許容できても、1桁の数値ミスは許容されない。
a one-pixel layout bug is forgivable, a wrong digit is not.
2コア機能
3外部依存関係
| Kind | Dependency / Endpoint |
|---|---|
| package | remotion(frame/interpolate/useVideoConfig) |
| package | d3-scale / d3-shape(值域映射与路径生成,仅取其数学,不用其计时器) |
| package | csv-parse(CSV → records,配 skip_empty_lines/trim) |
| cli | ffmpeg / ffprobe(仓库共享 contact-sheet.sh、probe-mp4.sh) |
| network | iart.ai(reference 尾部推广链接,具 utm 参数;仅读者点击才请求) |
4推奨統制措置と判定根拠
- scripts/: ローカルスクリプトまたはコマンドの実行: skillディレクトリ内に組み込みスクリプトなし。SKILL.md/referenceはagentに対し`node scripts/build-props.mjs`の実行を指示
- 网络端点: 外部ネットワークアクセスの宣言: 実行時にビジネスAPIの呼び出しはなし。データセットはユーザーからCSV/JSON形式で提供される。
- 本地工作目录: ファイルの読み書きと成果物の出力: ユーザープロジェクト内で`props/<name>.json`、`out/<name>.mp4`、`out/f-start.png`/`out/f-mid.p`を書き込み
5二重目の独立検証
- [discrepancy] SKILL.md 是否声明了 CSV 解析依赖 — SKILL.md 通篇未提 `csv-parse`(只把 CSV→props 指向 data-pipeline.md),而 reference 直接 `import { parse } from "csv-parse/sync"`;依赖清单在 SKILL.md 层面不完整。
- [discrepancy] race 伪代码是否与 reference 的 clamp 要求一致 — SKILL.md 片段 `const f = easeInOut(t - i);` 未把 f 夹到 [0,1],而 bar-chart-race.md 用 `Math.min(Math.max(tRow - i, 0), 1)` 并把它列入 pitfall 表(Last frame overshoots → Clamp f to [0,1]);直接照抄 SKILL.md 片段会末帧过冲。
- [discrepancy] 示例帧号与 reference 默认节奏是否对齐 — SKILL.md 验证示例给 `--frame=149 # last = durationInFrames - 1`(即 150 帧=5s 总长),而 reference 默认 SECONDS_PER_ROW=1.5 且示例 4 行 → 195 帧(末帧 194)。公式正确但示例数字与 reference 算例不一致。
- [ok] 数字格式化口径是否全文一致 — SKILL.md 与 data-pipeline/bar-chart-race 三处都用 Math.round() + Intl.NumberFormat(en-US) + font-variant-numeric: tabular-nums;percent 变体单独处理分数。
- [ok] 「D3 可用 scale 不可用 transition」的边界是否一致 — SKILL.md 明说 D3 仍适合 scales and shapes,只是不用它的 timers;bar-chart-race.md 实际只 import scaleLinear、无任何 .transition() 调用。
- [ok] 时长推导与 calculateMetadata 声明 — SKILL.md 要求数据相关时长在 calculateMetadata 计算,reference 用 (rows.length - 1) * SECONDS_PER_ROW * fps + hold * fps 推导总帧数,思路一致。
6結論
a97e1c50769273d4…