mcpField
C
caption-animation

caption-animation

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

关于 caption-animation

caption-animation 是一项将转录文本或旁白制作成逐词定时动画字幕的技能,适用于短视频等场景。它涵盖从时间戳令牌到 Remotion 或网页动画及字幕导出的流程。

主要功能

  1. 01逐词时间戳处理流程
  2. 02逐词弹入与当前词高亮
  3. 03字幕分页与布局指导
  4. 04画面内字幕及 SRT/VTT 导出
  5. 05基于帧的渲染验证

适用场景

  1. 01为竖屏视频制作卡拉 OK 字幕
  2. 02将转录字幕烧录到旁白视频
  3. 03用 Remotion 动画呈现 Whisper 或 SRT 文本
  4. 04为无障碍播放提供独立字幕文件

安装

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

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

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

附带文件README.mdreferences/word-timed-captions.md

详细介绍

字幕动画

将转录文本或旁白转化为按词定时、吸睛的字幕:每个词随音节弹出,高亮当前词,文本与旁白紧密同步。制作能提升 TikTok、Reels 和 Shorts 观看时长的字幕——清晰易读、紧跟节拍,并位于安全区域内。

适用场景

  • 在竖屏短视频(9:16)中逐词显示字幕或卡拉 OK 字幕。
  • 根据转录文本,将开放字幕烧录到旁白音轨上。
  • 将 Whisper/SRT 转录文本转换为 Remotion 或网页中的动画文本。
  • 将自动生成的字幕重新设计为品牌风格的 “Hormozi-style” 逐字呈现效果。

工作流程

阶段任务工具
转录音频 → 词级时间戳Whisper(@remotion/install-whisper-cpp)、AssemblyAI
规范化→ { text, startMs, endMs }[] 令牌@remotion/captions Caption 类型
分页将词分组为 1–4 词的“页”createTikTokStyleCaptions()
动画逐词弹出 + 当前词高亮Remotion spring() / CSS
放置安全区域、清晰易读的字体下方的布局规则
导出烧录字幕(MP4)和/或旁挂 SRT/VTT 文件Remotion 渲染 / 文件输出

两条不可妥协的规则

  1. 使用词级时间,而非行级时间。 只有每个词都落在对应音节上,卡拉 OK 字幕才会显得神奇。始终转录出词级时间戳;绝不要把一行的持续时间平均分配给每个词来伪造时间——偏差会立刻显现。
  2. 可读性优先于风格。 字幕需要在阳光下、静音时也能在手机上看清。优先使用粗体无衬线字体、粗描边或阴影,并确保高对比度;装饰其次。看不清的字幕只是装饰,不是字幕。

从转录文本获取词级时间

Whisper 会直接输出词级时间戳。将所有来源规范化为统一的扁平令牌结构,这样渲染器就无需关心词语来自哪里:

// Caption token — the one shape everything downstream consumes
type Token = { text: string; startMs: number; endMs: number };

// Parse an SRT cue block "00:00:01,200 --> 00:00:01,640" into ms
const toMs = (t: string) => {
  const [h, m, rest] = t.split(":");
  const [s, ms] = rest.split(",");
  return ((+h * 60 + +m) * 60 + +s) * 1000 + +ms;
};

如果只有句子级时间提示,请重新转录以获取词级时间——不要插值。对于清晰的人声音频,Whisper 的 medium.en 速度约为 large 的 2 倍,准确度也足够。

逐词弹出(Remotion)

以每个词自身的 startMs 为锚点,用弹簧效果驱动该词的缩放和不透明度。微小的过冲会让效果更显“鲜活”。

import { useCurrentFrame, useVideoConfig, spring, interpolate } from "remotion";

const Word: React.FC<{ token: Token; active: boolean }> = ({ token, active }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const enter = (token.startMs / 1000) * fps;            // word's own entrance frame
  const p = spring({ frame: frame - enter, fps, config: { damping: 12, mass: 0.6 } });
  const scale = interpolate(p, [0, 1], [0.6, 1]);        // pop from 60% → 100% (overshoots)
  return (
    <span style={{
      display: "inline-block",
      transform: `scale(${scale})`,
      opacity: interpolate(p, [0, 1], [0, 1]),
      color: active ? "#FFE45E" : "#FFFFFF",             // active-word highlight
      transition: "color 80ms linear",
    }}>{token.text}&nbsp;</span>
  );
};

通过检查 frame 是否位于每个令牌的 [startMs, endMs] 范围内来计算 active。高亮只使用一种强调色,或在当前词后方显示一个填充色“方框”——绝不要同时为每个词做颜色动画。

使用分页,不要堆成文字墙

每次显示 1–4 个词。使用 Remotion 的 createTikTokStyleCaptions()(或手动分组),并调整 combineTokensWithinMilliseconds:逐词显示时设为约 200–500ms,显示简短易读的短语时设为约 1000–1500ms。

import { createTikTokStyleCaptions } from "@remotion/captions";
const { pages } = createTikTokStyleCaptions({
  captions, combineTokensWithinMilliseconds: 1200,       // ~2–3 words per page
});

网页/CSS 弹出效果(不使用 Remotion)

对于 DOM/实时播放器,为每个词设置独立的 animation-delay,其值等于 startMs:

.word { display:inline-block; opacity:0; animation: pop .26s cubic-bezier(.34,1.56,.64,1) forwards; }
@keyframes pop { from { opacity:0; transform:translateY(.18em) scale(.7) } to { opacity:1; transform:none } }
.word.active { color:#FFE45E; }
words.forEach(w => { const el = mk(w.text); el.style.animationDelay = `${w.startMs}ms`; track.append(el); });

清晰易读的字体与安全区域布局(9:16,1080×1920)

属性值原因
字体粗体/特粗无衬线字体(Montserrat、Inter、Helvetica)在尺寸小、画面繁杂的屏幕上也清晰易读
字号56–80px(约占画面高度的 8%),最小 45px静音时在手机上也清晰易读
描边2–6px 实心黑色描边适用于任何背景
阴影柔和的投影,可作为描边的补充在明亮画面上增强分离度
大小写全大写或句式大小写;使用高对比度填充色增强冲击力和易读性
垂直位置画面中央区域,向下约 62–70%位于 UI 上方、主体动作下方
底部安全区底部约 280px / 15% 范围内留空避开字幕/CTA/音频 UI
两侧安全区限制在画面中央 80% 的宽度内避开右侧栏图标

将字幕放在画面中央区域——不要固定在最底部,因为平台 UI(用户名、音频、按钮)会占据那里。文字最多留出约 2 行的边距。

同步到旁白/解说

转录文本已经带有精确音频的时间信息。将同一段音频置于合成内容下方,并保持时间戳与音频使用相同的时间基准——字幕和语音会始终同步,无需手动微调。如果重新录制旁白,请重新转录;切勿手动调整偏移量。

烧录字幕与外挂 SRT/VTT

烧录字幕(开放式)外挂 SRT/VTT(隐藏式)
位置MP4 的像素中单独的 .srt/.vtt 文件
社交平台(TikTok/Reels)必须使用——效果有保障,且可设置样式平台通常会忽略
无障碍(ADA/WCAG)单独使用无法满足要求必须提供(可切换)
最佳实践为社交平台烧录动态字幕同时提供外挂文件,用于网页/SEO

两种都输出:为社交平台烧录动态字幕,并使用相同的词元生成纯文本 SRT/VTT,方便无障碍/SEO 播放。

输出检查清单

  • 词级时间戳(来自真实转录)——不要伪造均分时间。
  • 每个词弹出时带有轻微过冲;当前词使用一种强调色。
  • 每页 1–4 个词;绝不铺满整屏文字。
  • 粗体无衬线字体,56–80px,2–6px 描边加阴影,高对比度。
  • 放在中间区域,避开底部约 280px 和两侧安全区。
  • 将音频置于合成内容下方;时间戳使用音频的时间基准。
  • 为社交平台提供烧录字幕,并为无障碍需求提供外挂 SRT/VTT。

交付并验证(渲染静帧 → 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 合成内容的形式交付(<Composition> + zod schema + defaultProps)——所有词语动画均由 useCurrentFrame() 按帧驱动,绝不使用 Date.now() / Math.random() / 定时器。交付物 = out/*.mp4(已烧录字幕)+ 项目 + 外挂 SRT/VTT。默认采用 9:16 竖屏(1080×1920)。

验证流程——渲染静帧 → 检查 → 编码。 最容易出问题的是词语时间;在花时间编码之前,先检查精确帧。

# Stills at start / a sampled active-word frame / end — WITH SHIPPED PROPS (real tokens + audio)
npx remotion still Captions out/f-start.png --frame=0   --props='{"captionsSrc":"vo.json"}'
npx remotion still Captions out/f-mid.png   --frame=90  --props='{"captionsSrc":"vo.json"}'
npx remotion still Captions out/f-end.png   --frame=N   --props='{"captionsSrc":"vo.json"}'  # N = durationInFrames-1

# Inspect each PNG:
#  - the word highlighted at frame 90 is the word whose [startMs,endMs] contains 90/fps (no drift)
#  - burn-in legible: bold sans, stroke+shadow holds, no clipping
#  - 9:16 safe area: caption sits in the center band, clear of top ~12% and bottom ~20-35% (captions/CTA/audio UI) and the right action rail

npx remotion render Captions out/captions.mp4 --props='{"captionsSrc":"vo.json"}'   # encode once stills are right
npx remotion render Captions out/demo.gif --codec=gif                                # README proof clip

使用 npx remotion compositions 读取 durationInFrames/fps,并选取当前词帧和结束帧。

完成前:

  1. 帧 0、中间的当前词帧和最后一帧均能正常渲染——不能缺少字体/音频。
  2. 采样帧中高亮的是正确词语(帧数/fps 落在其词元时间范围内);不要伪造均分时间。
  3. 烧录字幕清晰可读(描边+阴影),且所有检查帧中的字幕都完全处于 9:16 安全区域内。
  4. 只能按帧驱动——不使用 Date.now() / Math.random() / 定时器。
  5. 已交付的 props(真实词元,而不仅是 defaultProps)能正确渲染;输出 MP4 和外挂 SRT/VTT,GIF 可选。

参考文件

  • references/word-timed-captions.md — 端到端构建指南:Whisper 转录和 Caption 类型、完整的 SRT 解析器、带当前词高亮的 Remotion 词级计时组件、手动分页、SRT/VTT 生成器、各平台安全区域映射,以及易读字体规格表。

评论

支持 Markdown