关于 presentation-video
presentation-video 是一项将演示文稿或幻灯片大纲制作成旁白视频的技能,面向需要自播放演示内容的团队和创作者。它用 Remotion 重建动画幻灯片,并按旁白音频长度安排时长与元素显示。
主要功能
- 01将幻灯片重制为动态图形
- 02根据旁白时长设置幻灯片长度
- 03按语音进度逐项显示内容
- 04为幻灯片添加转场与章节页
- 05渲染并检查 MP4 视频
适用场景
- 01将融资演示稿制作成自动播放视频
- 02把课程大纲转为旁白幻灯片视频
- 03使幻灯片动画与讲解音频同步
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ iart-ai/ data-animation-skills --skill presentation-video安装后在 Claude Code 中输入/presentation-video确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdreferences/sequencing.mdslide-components.md详细介绍
演示视频
将演示文稿或幻灯片大纲转化为配有旁白、自动播放的动画视频。每张幻灯片都会重新制作成动态图形——元素错落呈现、真正的转场、时长与旁白同步——而不是录制某人点击播放 PowerPoint 的画面。
适用场景
- 需要转化为自动播放视频的路演演示文稿、网络研讨会或课程模块。
- 需要制作成带旁白的动画幻灯片的要点大纲。
- 幻灯片时长必须与旁白精确同步的演示文稿(自动播放,无需点击)。
重新制作,而不是录制
屏幕录制会照搬演示文稿静态的视觉效果、鼠标抖动和突兀的幻灯片切换。用代码(Remotion)重新制作则可以实现:逐帧确定的元素呈现、真正流畅的转场、在任何分辨率下都清晰的文字,以及由音频时长决定的幻灯片播放时长。演示文稿成为脚本;视频则是全新的作品。
叙事脉络:整套演示文稿是一条论证,而不是 N 张幻灯片
在安排时间或动画之前,先理顺故事。能够自动播放的演示视频没有演讲者来帮观众接上断掉的思路——结构必须独立承载逻辑。先搭建主线,再把幻灯片作为主线上的节拍。
- 一条贯穿始终的主线。 用一句话说出演示文稿唯一的核心结论。每张幻灯片都必须推进这句话;如果没有,就删掉或移到附录。观众看一遍后应该能够复述这条主线。
- 每张幻灯片只传达一个信息。 一张幻灯片只讲一个观点——标题就是这个观点,应写成一句主张(“客户流失才是我们真正的增长负担”),而不是一个标签(“客户流失”)。两个观点就做两张幻灯片。
- 转场承载逻辑,而不只是动效。 幻灯片之间的切换应回答“接下来呢?”——因此、但是、这意味着、证据如下。让动效匹配逻辑变化:“此外”用轻柔淡出,“开启新篇章”则用更明显的章节推进。没有标示逻辑步骤的转场,只是装饰。
- 以引人入胜的内容开场,以行动号召收尾。 第一张幻灯片要在约 5s 内吸引注意力——用尖锐的问题、令人意外的数据或利害关系开场,而不是议程或停留的 Logo。最后一张幻灯片要明确提出唯一希望观众采取的行动(“启动试点”或“投资 200 万美元,获得 15% 股份”),并保持静止,作为总结画面。
矛盾 → 化解(路演主线)
路演是一场戏剧,而不是功能清单。按矛盾逐渐升高、继而化解的顺序编排,每一幕都为下一幕铺垫。
| 幕 | 节拍 | 作用 |
|---|---|---|
| 问题 | “问题出在哪儿/利害何在” | 让观众感受到矛盾 |
| 洞察 | “大家都忽略了什么” | 带来出人意料的转折,为解决方案赢得认同 |
| 解决方案 | “这是我们的答案” | 化解问题所引发的矛盾 |
| 证明 | “这是它奏效的原因” | 发展成果、演示、数据 |
| 请求 | “这是我们希望得到的” | 提出一个具体的行动号召 |
不要在问题尚未充分呈现之前就揭晓解决方案——缺少铺垫的解决方案听起来像一项功能,而不是解脱。将幻灯片对应到这些幕;如果缺少某一幕,论证就有漏洞。
节奏:让内容展开与旁白同步
主线随着时间推进,因此屏幕上的内容展开必须与旁白同步——时间由旁白掌控(见下一节),元素呈现要据此安排,让观众在听到台词的同时读到对应内容,绝不提前。每张幻灯片只讲一个信息,自然就能做到这一点:幻灯片恰好在旁白论述该观点的整个过程中承载它,然后通过转场标示进入下一步逻辑。某个观点的旁白太长,就拆分幻灯片,而不是加快内容展开。
唯一规则:时间由旁白掌控
每张幻灯片的时长必须恰好等于对应旁白音频的时长,再加上一小段余量。 测量每段旁白音频的实际时长,换算成帧数,并据此确定幻灯片时长。绝不要凭感觉估计“这张幻灯片大概 5s”——时长总会逐渐偏移,音频要么被截断,要么提前结束。
import { getAudioDurationInSeconds } from "@remotion/media-utils";
// In a Remotion calculateMetadata() / build step, per slide:
const sec = await getAudioDurationInSeconds(slide.audioUrl);
const HANDLE = 0.4; // breathing room after the voice ends
slide.durationInFrames = Math.ceil((sec + HANDLE) * fps);
然后,根据幻灯片的时长安排幻灯片内部的元素呈现——要点随着旁白说到相应内容时出现,而不是按固定间隔出现。
构建内容:用动画逐步呈现,而非装饰
构建内容的关键在于把握节奏:旁白讲到哪个元素,就显示哪个元素,让观众读到正在讲述的内容,而不是提前看到三行。让元素依次淡入;标题、徽标和背景形状保持静态。
| 选项 | 建议 | 原因 |
|---|---|---|
| 入场动画 | 淡入 + 上移 12–24px | 精致、平和;有“渐渐呈现”的感觉 |
| 单个元素的时长 | 0.25–0.5s | 足够快,节奏流畅;也足够慢,能让人看清 |
| 项目符号之间的间隔 | 0.15–0.4s | 依次呈现,而非一堵文字墙 |
| 避免使用 | 飞入、旋转、弹跳、打字机效果 | 看起来像装饰;会分散对旁白的注意力 |
通过 useCurrentFrame() 控制依次呈现的时间间隔,使其与渲染后的音频保持同步(完整组件请参见 references/slide-components.md)。
import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion";
const Bullet: React.FC<{ index: number; children: React.ReactNode }> = ({ index, children }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const STAGGER = 8; // frames between bullets (~0.27s @ 30fps)
const enter = frame - index * STAGGER; // each bullet starts later
const p = spring({ frame: enter, fps, config: { damping: 200 }, durationInFrames: 12 });
return (
<li style={{ opacity: p, transform: `translateY(${interpolate(p, [0, 1], [18, 0])}px)` }}>
{children}
</li>
);
};
幻灯片间的转场
使用 @remotion/transitions 中的 <TransitionSeries> 串联幻灯片。每张幻灯片都是一个 Sequence,其 durationInFrames 根据旁白时长确定;两张幻灯片之间放置一个 Transition。转场要克制(短暂淡入淡出或轻柔滑动)——内容才是主角,每次切换都使用繁复的擦除效果,很快就会令人厌倦。
import { TransitionSeries, linearTiming } from "@remotion/transitions";
import { fade } from "@remotion/transitions/fade";
<TransitionSeries>
{slides.map((s, i) => (
<React.Fragment key={s.id}>
<TransitionSeries.Sequence durationInFrames={s.durationInFrames}>
<Slide {...s} />
</TransitionSeries.Sequence>
{i < slides.length - 1 && (
<TransitionSeries.Transition
timing={linearTiming({ durationInFrames: 15 })}
presentation={fade()}
/>
)}
</React.Fragment>
))}
</TransitionSeries>
仅在章节分隔页使用更大幅度的转场(滑动/推入)——它表示“新主题开始”,也值得多一些动效。转场会占用两侧幻灯片的帧数;如果某张幻灯片的旁白必须完整播放,就要把转场时长加回该幻灯片的时长预算中。参见 references/sequencing.md。
标题页和章节分隔页
标题页和章节分隔页能为视频增添节奏,让观众在内容密集的幻灯片之间稍作喘息。把分隔页视为一张独立的短“幻灯片”:放置较大的章节编号/文字、章节标题,停留约 1.5–2s,并在两侧使用更明显的转场。分隔页的视觉风格(颜色、字体)要保持一致,让它像标点,而不是另一段视频。
保持文字易读并符合品牌风格
幻灯片文字密集,因此易读性至关重要。
- 调整字体大小,而非沿用演示文稿的字号。 在笔记本电脑上观看演示文稿 ≠ 在信息流中观看视频。1080p 下,正文增大到 ≥ 28–32px;标题设为 56–80px。如果必须用 16px 的脚注才能放下内容,就应该拆成两张幻灯片。
- 每张幻灯片只讲一个观点。 如果某张幻灯片的旁白太长,就拆分内容——不要缩小字体来容纳。
- 确保对比度并留出安全边距。 对比度保持 ≥ 4.5:1;所有内容都放在中央 90% 的区域内,避免被平台裁切。
- 在主题对象中锁定品牌风格。 颜色、字体、徽标和间距都放在一个
theme中,确保每张幻灯片风格一致,更新品牌时只需改一行。
export const theme = {
bg: "#0B0B12", fg: "#F5F5F7", accent: "#6C5CE7",
font: "Inter, system-ui, sans-serif",
titlePx: 72, bodyPx: 30, maxWidthPct: 90,
} as const;
工作流
- 先列大纲 → 再写脚本。 一张幻灯片 = 一个观点 + 对应的完整旁白。这是唯一可信来源。
- 为每张幻灯片生成/录制旁白。 每张幻灯片对应一个音频文件(TTS 或录音)。
- 测量音频 → 设置幻灯片时长。 使用
getAudioDurationInSeconds将音频时长换算为帧数(遵循上述规则)。 - 构建幻灯片组件,并通过
useCurrentFrame()控制元素依次呈现。 - 使用
<TransitionSeries>串联幻灯片;添加标题页和章节分隔页。 - 将每张幻灯片的音频挂载在其
Sequence内,确保语音和画面绝不会错位。 - 检查易读性和品牌风格,然后渲染为 MP4。
输出检查清单
- 每张幻灯片的
durationInFrames都根据旁白音频确定,而非估算。 - 每张幻灯片的音频都挂载在自己的
Sequence内(画面和语音保持同步)。 - 项目符号通过
useCurrentFrame()依次呈现;标题和徽标保持静态。 - 呈现效果为淡入并上移,时长为 0.25–0.5s,不添加装饰性效果。
- 幻灯片间的转场保持克制;幅度较大的转场仅用于章节分隔页。
- 1080p 下正文 ≥ 28px,对比度 ≥ 4.5:1,内容位于中央 90% 的区域内。
- 每张幻灯片只讲一个观点;旁白太长就拆分幻灯片,而不是缩小字体。
交付与验证(渲染静帧 → 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>+ zodschema+defaultProps),所有动画均由帧驱动(不使用计时器 /Date.now()/Math.random());每张幻灯片的音频都挂载在各自的Sequence内。 - 交付物 = 渲染生成的
out/*.mp4(以及项目本身,方便用户使用新的幻灯片 / 旁白重新渲染)。 - 幻灯片时长根据旁白推导——在
calculateMetadata中计算总时长和每张幻灯片的durationInFrames(它会测量每段音频),切勿手动设置。
验证流程——渲染静帧 → 检查 → 编码。 先渲染单帧(成本低,无需视频编码),只有确认每张幻灯片的构建和阅读效果都正确后才进行编码。
# 1. Frame-exact stills — with the SHIPPED props. Land one inside each slide's settled state:
npx remotion still Deck out/f-title.png --frame=20 --props='{"slides":[...]}' # title card settled
npx remotion still Deck out/f-mid.png --frame=240 --props='{"slides":[...]}' # a content slide, bullets built
npx remotion still Deck out/f-end.png --frame=899 --props='{"slides":[...]}' # last frame = durationInFrames - 1
# 2. Inspect each PNG — FIDELITY (slide text exact, numbers/labels correct, brand colors/fonts applied)
# AND artifacts (text overflow, body type too small, content outside center 90% safe-crop, missing
# font, low contrast, a bullet caught mid-build where the slide should be settled).
# 3. Only after the stills check out, encode:
npx remotion render Deck out/deck.mp4 --props='{"slides":[...]}'
- 使用
npx remotion compositions查看演示文稿的durationInFrames/fps,并确定结束帧;采样每张幻灯片停留期间的静帧,而非转场中的画面,以评估构建完成后的效果。 - 数据驱动 / 批量处理:在渲染所有变体前,先通过静帧验证一个有代表性的演示文稿(真实幻灯片 + 旁白)——一次性发现字号或溢出问题,而不是让每个演示文稿都各自踩坑。
- 免费生成 README 演示 GIF:
npx remotion render Deck out/demo.gif --codec=gif。
完成前:
npx remotion still能在标题页、内容页和最后一帧干净地完成渲染——没有错误,也没有缺失字体。- 幻灯片文本准确无误,1080p 下正文 ≥ 28px,对比度 ≥ 4.5:1,且每一帧的所有内容都位于中心 90% 区域内。
- 仅使用帧驱动机制——不使用
Date.now()/Math.random()/ 库计时器(确保在 CI 中保持确定性)。 - 实际交付的 props 能正确渲染(而不只是
defaultProps)——幻灯片正确、旁白已挂载、时长由音频推导。 - 完成 MP4 编码并确认可播放(语音与画面同步);(可选)为 README 渲染 GIF。
参考文件
references/slide-components.md— 完整且可运行的 RemotionSlide、错落呈现的BulletList、动画TitleCard和SectionDivider组件,包含共享主题,以及通过<Audio>挂载的每张幻灯片旁白。references/sequencing.md— 完整的演示文稿序列器:测量每段旁白并设置总时长及每张幻灯片时长的calculateMetadata、<TransitionSeries>组装、转场帧预算核算,以及从 JSON 演示文稿架构到视频的数据流。