mcpField
P
presentation-video

presentation-video

github.com/iart-ai创意与媒体151.3k 次安装18 周前更新
代码仓库

关于 presentation-video

presentation-video 是一项将演示文稿或幻灯片大纲制作成旁白视频的技能,面向需要自播放演示内容的团队和创作者。它用 Remotion 重建动画幻灯片,并按旁白音频长度安排时长与元素显示。

主要功能

  1. 01将幻灯片重制为动态图形
  2. 02根据旁白时长设置幻灯片长度
  3. 03按语音进度逐项显示内容
  4. 04为幻灯片添加转场与章节页
  5. 05渲染并检查 MP4 视频

适用场景

  1. 01将融资演示稿制作成自动播放视频
  2. 02把课程大纲转为旁白幻灯片视频
  3. 03使幻灯片动画与讲解音频同步

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/iart-ai/data-animation-skills --skill presentation-video

安装后在 Claude Code 中输入/presentation-video确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
references/
sequencing.md
slide-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;

工作流

  1. 先列大纲 → 再写脚本。 一张幻灯片 = 一个观点 + 对应的完整旁白。这是唯一可信来源。
  2. 为每张幻灯片生成/录制旁白。 每张幻灯片对应一个音频文件(TTS 或录音)。
  3. 测量音频 → 设置幻灯片时长。 使用 getAudioDurationInSeconds 将音频时长换算为帧数(遵循上述规则)。
  4. 构建幻灯片组件,并通过 useCurrentFrame() 控制元素依次呈现。
  5. 使用 <TransitionSeries> 串联幻灯片;添加标题页和章节分隔页。
  6. 将每张幻灯片的音频挂载在其 Sequence 内,确保语音和画面绝不会错位。
  7. 检查易读性和品牌风格,然后渲染为 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> + zod schema + 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。

完成前:

  1. npx remotion still 能在标题页、内容页和最后一帧干净地完成渲染——没有错误,也没有缺失字体。
  2. 幻灯片文本准确无误,1080p 下正文 ≥ 28px,对比度 ≥ 4.5:1,且每一帧的所有内容都位于中心 90% 区域内。
  3. 仅使用帧驱动机制——不使用 Date.now() / Math.random() / 库计时器(确保在 CI 中保持确定性)。
  4. 实际交付的 props 能正确渲染(而不只是 defaultProps)——幻灯片正确、旁白已挂载、时长由音频推导。
  5. 完成 MP4 编码并确认可播放(语音与画面同步);(可选)为 README 渲染 GIF。

参考文件

  • references/slide-components.md — 完整且可运行的 Remotion Slide、错落呈现的 BulletList、动画 TitleCard 和 SectionDivider 组件,包含共享主题,以及通过 <Audio> 挂载的每张幻灯片旁白。
  • references/sequencing.md — 完整的演示文稿序列器:测量每段旁白并设置总时长及每张幻灯片时长的 calculateMetadata、<TransitionSeries> 组装、转场帧预算核算,以及从 JSON 演示文稿架构到视频的数据流。

评论

支持 Markdown