关于 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确认已加载。
详细介绍
数据故事图表
一张图表,按照故事的展开顺序逐步构建和呈现。数字是主角:顶部的大号计数器、随数字变化的折线或柱形,以及说明变化原因的标注图钉。整体应像解释性新闻报道:清晰、沉稳,并有一两个戏剧性时刻。
选项
| 选项 | 默认值 | 备注 |
|---|---|---|
| 格式 | 竖屏 1080×1920 | 或横屏 1920×1080 |
| 时长 | 15–20 s | 每个数据节拍约 3–5 s |
| 图表 | 折线图 | line(随时间变化)、bars(比较)、stacked(拆解总量)、counter(一个大数字) |
| 基调 | 新闻编辑风 | editorial(默认,浅色纸张)、dark(藏青色、发光强调色)、punchy(节奏更快,大事件时切换颜色) |
简报(规划前回答)
- 观众应该记住的那个数字或变化是什么?
- 数据是什么(粘贴数据或提供文件)?单位、时间范围、来源是什么?
- 意外之处是什么:激增、骤降、交叉,还是隐藏成本?
- 哪 2–4 个时刻值得用标注图钉说明?
- 结尾文案是什么?
只使用用户提供的真实数字,或根据其数据计算得出的数字。切勿编造数据。如果某个数字是估算值,请在屏幕上注明。
阅读节拍表(与节拍列表一同编写)
对于每个节拍,写明观众必须理解的一件事(“机器人不会飞”“缓存未命中”)以及内容保持可读的时长。一次只呈现一条信息。文字时长下限:标签至少停留 0.8 s;句子完全显示后,每个词约需 0.3 s。如果文字来不及读完,就删减文字,不要加快节奏。
动效规范
- 动效应有因果关系:屏幕上的每个动作都有原因(点击、坠落、数据包到达),接下来的动作由此引出。
- 为整段视频选定一个主要运动方向,并始终保持一致。其他方向用于传达含义:向上表示成功或结论,缩小表示“全貌如下”。
- 一个场景的退出应为下一个场景的进入做铺垫:沿相同轴线、朝相同方向、以相近速度运动。让一个物体贯穿场景切换,而不是交叉淡化。
- 图示、图表或文字不要添加无意义的填充动效(漂浮、呼吸、摇晃)。如果没有内容需要移动,就保持静止;一次刻意的镜头漂移就足够。
- 缓动:进入时使用平滑的缓出(
power2.out到power4.out),退出时使用更快的缓入(耗时约为进入的 75%)。进入动效最多 0.8 s。同一组动效的总错开时间最多 0.5 s。back.out仅用于小幅弹出;不要使用弹性或弹跳效果。 - 关键时刻一次只动一个元素;观众的视线一次只能跟随一个元素。
- 在重点揭晓前加入 0.3–0.75 s 的静止停顿。
样式规则(始终遵守)
- 每个场景只使用一种图表类型,并突出一组数据。其他数据系列显示为灰色。
- 按时间或故事顺序逐步呈现图表:折线从左向右绘制,柱形按顺序增长;内容尚未讲到时不要提前显示。
- 大号计数器显示当前值,并随图表逐步变化(使用等宽数字)。
- 当数据超出坐标轴范围时,在屏幕上重新缩放坐标轴(对比例和标签进行缓动插值)。这通常是视频中最精彩的时刻。
- 用标注图钉标出 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)。
设置(一次性)
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,说明文字位于左下方或图表右侧。
- 标记点位于对应数据点上方,并由虚线连接。确保它们不遮挡计数器和说明文字。
如何制作动画(支持安全跳转)
将数据写成函数或数组,并根据一个时钟值 t 绘制整张图表。用一个补间动画驱动时钟,并在其 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 的样本绘制路径(每秒数据约 30 个样本) |
| 折线下方的区域 | 将同一路径闭合至基线,使用不透明度为 12% 的强调色 |
| 柱形增长 | 高度从 0 开始补间,间隔 0.08 s,使用 power3.out |
| 计数器 | 显示当前值;小于 10 时保留 1 位小数,大于等于 10 时显示整数 |
| 坐标轴缩放 | yMax(t) 在 1–1.5 s 内从旧最大值平滑过渡到新最大值,同时更新刻度标签 |
| 标记点弹出 | 在对应时刻使用 back.out(2.5),缩放比例从 0.5 变为 1 |
| 强调 | 关键数字出现时,计数器以 1.12 的比例缩放并往返 |
增添吸引力
- 首帧就显示计数器、坐标轴和问题。
- 在半秒内同时触发惊喜时刻的坐标轴缩放、标记点和说明文字变化。
- 用一个小标记(圆点、图标或角色)沿着折线前端移动,让观众知道该看哪里。
- 区域填充和前端数据点上的柔和强调光晕能增加纵深感,又不会显得杂乱。
- 说明文字用简短句子,并将数字设为强调色(“随后: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 字体的 <head>,并添加 html,body{margin:0;width:1080px;height:1920px;overflow:hidden}。在同一时间轴上添加提示标签、说明文字和标记点。
可配置参数
format · duration · chart · tone · data(数组或函数)· units · pins(时间、标签、颜色)· captions(时间、文本)· rescaleAt。
避免
- 屏幕上的每个数字都必须来自用户数据或注明出处的来源。在构图旁保留一份简短的事实清单(数字、来源)。任何示意性内容都要在屏幕上标明。
- 将虚构或“示意性”数字当作真实数据呈现。
- 同时突出多个系列,或使用彩虹色盘。
- 使用浅灰色标签(无法通过对比度检查,在手机上也难以辨认)。
- 图表杂项:3D 柱形、粗重网格线,以及有直接标签就不需要的图例。
- 坐标轴突然跳变而没有平滑过渡。平滑缩放,让观众看清变化过程。
- 让标记点或说明文字遮挡折线上的关键点。
故障模式
| 表现 | 原因 | 修复方法 |
|---|---|---|
| 渲染时线条跳动或闪烁 | 根据挂钟时间或逐帧随机值绘制 | 在 onUpdate 中只根据时钟 t 绘制 |
| 图钉标签溢出胶囊框 | 预估的胶囊框宽度过小 | 在 28 px 字号下,宽度 ≈ 36 + 字符数 × 17(全大写时 × 23);或者等字体加载后再测量 |
| 引导语和计数器重叠 | 两者位置太近 | 引导语基线与计数器顶部之间留出 30+ px |
| 惊喜效果显得平淡 | 没有缩放、图钉或说明文字变化 | 在 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–10 分:吸引力(第一秒)、手机屏幕宽度下的可读性、动态效果质量、构图、准确性,以及“静音测试”(观众能否在无声的情况下用一句话概括内容?)。只修改得分为 7 分及以下的项目,重新截取快照并重复检查,直到所有项目都达到 8 分或以上。如果可以,请安排单独的只读审核来评分,避免给自己的作品打分。
| 不要这样做 | 应当这样做 |
|---|---|
| 在两个繁杂的布局之间交叉淡化 | 先让旧内容退出,再引入新内容;或者让一个对象贯穿其中 |
| 同时让多个元素移动 | 错开动作,每个时刻只突出一个焦点 |
| 用漂浮或摇晃填补停顿 | 保持静止,或者让角色行动 |
| 为了追求“活力”而添加粒子、彩虹渐变或 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,以 42,000 个点构成的印度尼西亚 81 年历史点状地图:https://x.com/sonnylazuardi/status/2103511590884815282
- @aakashgupta,用堆叠条形图和计数器分解一份薪酬报价:https://x.com/aakashgupta/status/2102998741359988832
- 灵感改编自 HyperFrames 动态设计原则、无露脸解说技能、JohnHeibel/ClaudeAnimationBase 的动画指南、howseen-ai/claude-motion-design 的批评迭代流程以及 latent-spaces/brag(并非照搬)。