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()),但不要用它的计时器。

将数据映射为像素

图表由两个函数构成:数值比例尺和帧插值器。将它们分开处理。

层工具用途
数值比例尺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;

除非是稳定的时间轴(时钟、年份计数器),否则不要对观众正在读取的数值做线性插值——标题数字的线性变化会显得很机械。

计数器/数字滚动效果

先对底层数值进行插值,再在渲染时格式化。必须做到两点:格式化前先取整(避免出现 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.5s在动画开始前,让观众读懂坐标轴和单位
每行数据(竞赛)0.3–0.6s速度足以营造动感,同时又能看清领先者
强调时刻+0.5–1s 慢放聚焦于交叉、纪录或拐点
最终状态停留 2–3s留出时间让观众理解重点并截图

逐步展示数据——先展示一个系列,再展示下一个——而不是一次性展示全部数据;观众一次只能跟踪一个移动对象。

标签与注释

没有上下文的数字只是噪声。始终渲染:每个条形/数据点上始终可见的数值标签(随数值变化)、时间指示器(年份/日期),以及轴单位。只在关键时刻(交叉、峰值)添加说明标注——指出拐点,然后让标注淡出。

模板 × 数据 — 批量输出

代码驱动图表的优势:一个设计,多组数据。将数据集设为输入属性,而不是硬编码常量,然后为每个文件渲染一次。

// 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

将颜色、字体和布局集中在单个主题对象中,让 50 个视频保持品牌一致,只改变数字。CSV→属性的解析、验证和完整批处理脚本,请参阅 references/data-pipeline.md。

输出检查清单

  • 每个动画值都是 useCurrentFrame() 的纯函数;不使用库计时器。
  • 数字经过舍入和格式化(Intl.NumberFormat),数字使用 tabular-nums。
  • 排名变化条带使用稳定的键;排名通过插值实现平滑过渡。
  • 坐标轴/单位只标注一次;数值标签随数值变化而动画。
  • 开场停留时间充足、每行节奏清晰易读、结尾停留 ≥2s。
  • 数据集是输入属性 — 一个模板即可渲染文件夹中的每个 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,因此检查静帧必不可少:一像素的布局错误可以接受,但数字错误不行。

输出约定:

  • 一个已注册图表的 Remotion 项目(<Composition> + zod schema + defaultProps),所有动画均由帧驱动(不使用计时器/Date.now()/Math.random(),不使用 Chart.js/D3 .transition() 时钟)。
  • 交付物 = 渲染后的 out/*.mp4(以及项目,以便用户使用新数据集重新渲染)。
  • 时长取决于数据(N 行 × 每行帧数)?在 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 个文件之前,先通过静帧验证一个有代表性的数据集 — 一次发现高度比例或列映射错误,胜过在 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 比例尺坐标轴。另附原生 Canvas 版本。
  • references/data-pipeline.md — CSV/JSON → 类型化属性的解析与验证、主题对象模式、动画计数器/线条显现方案,以及用于生成 N 个视频的模板 × 数据批量渲染脚本。

评论

支持 Markdown