mcpField
S
stopmotion-video

stopmotion-video

github.com/kosta4a创意与媒体613 次安装3 天前更新
代码仓库

关于 stopmotion-video

此技能用于制作与音乐同步的定格动画风格视频,以节拍和小节安排画面变化,并提供面向 Remotion 的制作流程与规则。

主要功能

  1. 01按八分音符安排画面姿态
  2. 02将页面变化对齐小节线
  3. 03让点击声和画面变化落在鼓点
  4. 04使用 Remotion 渲染和检查

适用场景

  1. 01制作与音乐同步的发布影片
  2. 02围绕乐曲段落规划场景
  3. 03用节拍器检查姿态时序

安装

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

$npx skills add https://github.com/kosta4a/stopmotion-video --skill stopmotion-video

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

包含的文件

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

assets/remotion/
beat.ts
transitions.tsx
references/
checking.md
clock.md
hits.md
live.md
poses.md
skipping.md
song.md
sound.md
type.md
scripts/
duck.mjs
find-tempo.mjs
make-metronome.mjs

详细介绍

定格动画视频

使用此技能制作的视频会随着歌曲节奏呈现定格动画效果。每个姿势保持一个八分音符,然后所有内容一起变化。页面在小节线上切换。每次点击都落在鼓点上,大幅变化落在强劲的底鼓上。帧率有意保持较低。当某个动作看起来不够流畅时,应调整姿势间隔,而不是抹平节拍。

这些规则源自一部时长 47 秒的发布影片,该影片剪辑到一首 169.825 BPM 的鼓打贝斯曲目上,历经数百轮音符调整。影片以 30fps 播放,每个八分音符保持一个姿势,即每秒 5.66 个姿势,每个姿势持续 5 或 6 帧。这里的数值来自这部影片;live.md 中的数值则来自一个按节拍器播放影片场景的网页。若某条规则适用于更广泛的情况,文中会特别说明。

术语

  • 姿势:一幅保持不变的画面,对应歌曲的一个八分音符。代码中称为一个节拍(STEP_MS、useBeat)。
  • 歌曲节拍:BPM 所计数的拍子,对应一个四分音符。它持续两个姿势,并响起一次节拍器提示音。
  • 小节:四个歌曲节拍,即八个姿势。在鼓打贝斯中,从 0 开始计数,底鼓落在姿势 0 上,军鼓落在姿势 2 和 6 上。
  • 鼓点:歌曲中的鼓声。强劲的底鼓是开启一个乐句的底鼓(在影片曲目中,每个奇数小节都会出现)。
  • 编写时长(毫秒):代码中的时间。它们以 200ms 的姿势为基准编写,播放时会加快 SPEED 倍(SPEED = TEMPO / 150)。
  • 时间戳采用 m:ss.ff 格式,其中 ff 表示帧数。Remotion Studio 以此格式显示时间,音符也以此格式到达。

执行原则

  • 有意保持较低的帧率。 切勿为了让动作更流畅而抹平节拍,应调整动作的姿势间隔。
  • 所有内容都保持一个姿势的时长,并一起变化,包括转场。任何内容都不能自行计时:嵌入的动画或滚动页面也要随姿势逐步变化。唯一的例外是打字:它会从当前姿势开始,以快于节拍的速度进行,就像实际打字一样。
  • 以歌曲作为时钟。 在绘制分镜前先测量歌曲。按照小节安排页面,在鼓点上安排动作。
  • 整个视频只使用一个时钟。 按视频的帧数计算姿势,绝不要按场景的帧数计算。
  • 以整小节为单位移动。 按整小节调整页面时长,或者在下一个小节线之前从其他地方补回减少的姿势。否则,后续所有内容都会偏离歌曲节奏。
  • 不要留出空闲姿势。 页面上的内容完成后,只保持到观众有足够时间理解为止。
  • 变化类型要少、方向要一致,不要使用淡入淡出。
  • 通过测量进行检查。 每个姿势截取一张静帧,在渲染后的音频中测量时间,并剪辑一版带节拍器的版本。不要凭目测判断同步情况。

默认使用 Remotion 工具

除非项目已有其他视频制作方式,否则建议使用 Remotion 制作视频。开始制作分镜前说明这一点及原因,只有用户要求使用其他工具时才更改。在网页上,改为实时播放(参见 live.md);在动态图形工具中,规则仍然适用,但辅助工具不适用。

  • 适用原因。 每一帧都是根据帧编号计算得出的 React 组件,因此,无论一个姿势落在哪一帧,都能精确呈现,而且每次渲染结果都相同。渲染时会跨多个标签页乱序绘制帧,只有由帧驱动的视频才能经受这种处理。assets/remotion/ 中的辅助工具是为它编写的,它的 Studio 会以 m:ss.ff 格式显示时间,正是表格中使用的格式。
  • 设置。 使用空白模板运行 npx create-video@latest。将 remotion 和每个 @remotion/* 包固定到同一个确切版本(影片使用的版本:4.0.532),并使用 TypeScript 5.x(clock.md §7)。使用一个 1920×1080、30fps 的合成。将 beat.ts 和 transitions.tsx 复制到 src/,并设置 TEMPO。
  • 配置。 在 remotion.config.ts 中:将帧设为 JPEG 格式,质量设为 95(默认的 80 会使平坦背景和细字体的边缘变粗糙);使用 BT.709 色彩(这样播放器显示的颜色就会与浏览器一致);并启用覆盖。
  • 预览和渲染。 使用 npx remotion studio 进行创作;使用 npx remotion render <Comp> out/<name>.mp4 渲染 1080p,添加 --scale=2 可渲染 4K;使用 --frames=<first>-<last> 或 npx remotion still <Comp> --frame=<n> 检查一段帧或一个姿势。

决策顺序

从上到下依次执行。每一步都会限定下一步。

  1. 提出工具。 使用 Remotion,除非项目已经通过其他方式制作视频;先按上述说明完成设置,再做其他事情。
  2. 测量歌曲。 根据起音找出速度,并检查所用片段内的节拍网格是否稳定。标记小节和乐句、鼓点消失和回归的位置,以及重重的底鼓。将片段精确剪到采样点,起始位置比乐句第一个底鼓早一点(23ms)。参见 song.md。
  3. 设定时钟。 一个姿势就是歌曲的一个八分音符。以 200ms 的姿势为基准制作,使用 SPEED = TEMPO / 150,以 30fps 渲染,并按整段视频的帧数计算姿势。参见 clock.md。
  4. 按小节安排页面。 以姿势为单位编写分镜。每页由整数个姿势组成,并从小节线开始。在制作动画前,先将歌曲中的事件映射到姿势。
  5. 安排节拍点。 代码按八分音符分段输入,标题和按住退格键则从各自的姿势开始,以快于节拍的速度进行。点击声和爆破声放在军鼓上,落地和新页面放在强拍上,大幅变化放在重重的底鼓上。铺垫放在安静处,落点则出现在鼓点回归时。参见 hits.md。
  6. 安排姿势间隔。 根据每个动作所需的姿势选择曲线。动作的第一个姿势就是它迈出的第一步,每个姿势都要带来可见的移动。动作要干脆到位,不要缓慢挪动。参见 poses.md。
  7. 切换页面。 可在节拍上硬切、切掉地面、像铺开纸张一样覆盖页面、让镜头进入一种颜色、从内部填充、通过一个形状打开,或使用像素块。绝不使用淡入淡出。参见 hits.md。
  8. 设置文字。 字号越大,字距越紧,行距也要相应收紧。每个姿势下,每个词与背景的对比度都必须达到 4.5:1。标题应逐字输入,而不是淡入。参见 type.md。
  9. 制作声音。 每个点击声都要精确到采样点,放在歌曲自身的节拍点上;所有点击声都要混入一条从歌曲起始处开始的音轨。降低点击声下方的音乐音量,并保留一份带节拍器的剪辑用于检查。参见 sound.md。
  10. 决定在哪里跳过节拍。 可使用骤停、闪避、停留、骤变、剪掉完整小节,或在歌曲节拍上结束。参见 skipping.md。
  11. 检查。 制作联系表,测量同步情况,并观看带节拍器的剪辑。将备注记录为映射到姿势的时间戳。参见 checking.md。
  12. 要在浏览器中实时播放? 使用节拍器遵循同样的规则:页面时间只用一个时钟,画面比声音提前 25ms,声音根据音频时钟提前安排;没有歌曲时,每次变化都要落在点击声上。参见 live.md。

规则一览

时钟
项目规则
帧率30fps,这是有意为之。平滑的 60fps 变体可以沿用同一份分镜(clock.md §8)
姿势歌曲的一个八分音符:在 169.825 BPM 时为 176.65ms,每秒 5.66 个姿势,以 30fps 计为 5 或 6 帧
制作基准所有时间都以 200ms 的姿势为基准,单位为毫秒;SPEED = TEMPO / 150;使用 ms() 转换为帧,嵌入式时钟使用 seconds()
姿势 n 的第一帧ceil(n × frames per pose),按整段视频的帧数计算(beatFrame)
页面由整数个姿势组成,从小节线开始(姿势数可被 8 整除)
更改时长按整小节调整,或在下一个小节线之前提前结束姿势
Studio 中拖动拖动轨道会写入一个 from 偏移量,使画面相对于声音发生偏移:将其移除
间距
移动曲线姿势
进出(镜头移动、经过、下划线)easeInOutSine六个姿势中依次变化 7、18、25、25、18、7%
从画面外进入缓出否则,缓慢起步会发生在视野之外
一页覆盖在另一页上easeOutSine三姿势过渡中依次变化 38、70、92%
缩放、线条加粗、开口变大每个姿势按相同倍率增长缩放时,目标沿直线移向中心
少姿势动画三次缓入缓出(3、12、35、35、12、3:中段跃进);强缓出(在第一个姿势落定:三姿势过渡为 78、97、100%,使用 moved() 移动时为 88、99、100%)
与节拍同步的元素
元素规则
点击按下一个姿势,落在底鼓或军鼓上;光标在最后一次点击后的一个姿势消失
插入符输入时保持实心,之后亮两个姿势、灭两个姿势
旋转指示器每个姿势最多转四分之一圈;每个姿势转过半圈以上会看起来像在反向旋转
翻滚标记转动角度等于其移动距离除以半径,在 easeOutSine 上直立落定
计数器数字在一个姿势内以强缓出出现,使用等宽数字,整数字时停留两个姿势
标题从其姿势开始,以快于节拍的速度打出:每三帧一个词;长行每 25ms 一个字母(手动制作)
代码每个姿势以不均匀的 2 到 4 个字符为一组打出
按住退格键连续删除,每 25ms 一个字母(手动制作),也包括换行符
镜头升起升起,在顶部静止停留两个姿势,然后降下
从画面外进入移动后才绘制
手动放置偏差不超过该姿势移动距离的十分之一;停止后不再偏移;在几像素内落定一次
节拍重音
歌曲中的位置画面中的表现
八分音符分组输入的代码、移动的步进
军鼓点击、弹出
强拍落定、新页面、页面的第一个动作
强劲底鼓大变化:页面落定、最后一页完整显示、最后一次点击
鼓声停下构建
鼓声重新响起落定

点击必须踩在节拍上。过渡可以不踩拍。

声音
项目规则
点击位置精确到采样点,落在歌曲的重音上(影片中的重音比对应姿势晚 25ms)
点击音频文件滴答声前留出 40ms 静音,这样裁切到采样点时不会把它切掉
点击音轨所有点击预先合并到一条从歌曲开始处起播的音轨中
闪避重要点击周围静音;其他点击下方以 45% 音量持续 160ms;淡入淡出 10ms
节拍器歌曲每个节拍的重音处都发出点击声,小节的第一个节拍声音更高,音乐音量设为 20%;导出时不包含
字体
字号字符间距行距
小于 20px0;等宽大写字母 +2%1.4–1.5
20px 及以上−1%约 1.4
32px 及以上−2%约 1.25
56px 及以上−3%1.1–1.2;两行展示标题可低至 0.9

每个姿势中,所有文字与背景的对比度都必须至少达到 4.5:1。默认使用一个字体系列(示例中使用 Geist 和 Geist Mono),采用 tactile-design 的做法,以黑字白底;影片本身则将展示字体与正文字体搭配,并为页面设置纯色背景。计数器使用等宽数字。标题要逐字打出,不能淡入。

在浏览器中实时运行
项目规则
时钟使用 beat = anchorBeat + elapsed s × BPM / 60 × 2,以 performance.now() 为准;速度变化时在当前时刻重新锚定;每个场景都是拍点的纯函数
画面提前 25ms 读取拍点;每个场景都在动画帧内提交(flushSync)
声音每 25ms 运行一次计时器,按音频时钟安排接下来的 120ms(getOutputTimestamp());窗口以音频时间测量,并计入输出延迟
画面内只让屏幕上可见的内容动起来;屏幕上最显眼的示例会发出声音
启动加载时尝试启动,之后在首次按下按钮或按键时启动;启动声音的那次按下不会将其静音(600ms);会记住静音状态
无歌曲每次变化都落在拍点上:移动可以踩在八分音符上,但必须从拍点开始并落在拍点上;移除该拍点自身的节拍器声
节拍器轻柔的木质敲击声(880Hz,持续 50ms;小节首拍为 1320Hz),没有音乐时不发出低沉重击,所有声音都经过同一个限制器
暂停在最近的拍点暂停:继续播放到该拍点,或在 160ms 内平稳退回
减少动态效果显示一个静止姿势来表达概念;只有按下声音按钮后才开始移动
文字文字对比度为 4.5:1,具有意义的标记为 3:1;缩放场景中最小的文字渲染后至少为 9px

何时跳过拍点

节拍网格是严格的,因此打破它是一项有意为之的选择。以下每种情况的理由都记录在 skipping.md 中。

  • 在重要拍点附近让音乐静音。 从拍点前 50ms 到下一个小节线期间静音,并在两端各用 10ms 淡入淡出。只听到拍点声,之后音乐随下一小节恢复。
  • 在其他拍点声响起时降低音乐音量。 将音乐音量降至 45%,持续 160ms,为拍点的低沉重击留出空间。
  • 在下一个内容开始前,单独停留一个歌曲拍点显示结果。 例如,复制后响起一声滴答。
  • 在上升到顶点时,连续保持两个相同姿势。
  • 在整拍数上连续保持两个姿势,然后再继续。
  • 将落在拍点之间的音符,移至最近的重音拍点。
  • 截去整小节,即使音符要求的时长更短也照做。说明这样做的代价。
  • 在歌曲拍点上结束。 最后一页停留约 2 秒,音乐在最后一个姿势期间渐弱。否则就在动作结束后立即结束。

绝不发布

  • 为掩盖间隔不当的移动,在拍点之外平滑运动
  • 场景从自己的第一帧开始计数姿势
  • 页面无理由地从小节线以外开始
  • 时长变化不是整小节,且未在下一个小节线前恢复
  • Studio 的 from 偏移留在合成树中
  • 拍点声偏离底鼓或军鼓,或拍点声被放在帧上而不是对齐到采样
  • 短促拍点声在各自的帧上单独启动,而不是使用一条预先混制的音轨
  • 移动的第一个姿势与离开时的姿势相同,或最后几个 px 缓慢挪动
  • 帧外的内容在移动之前就已绘制
  • 微调器或转轮每个姿势转动超过半圈
  • 淡入淡出,或饱和背景之间的交叉淡化
  • 背景变化速度超过每秒 3 次闪烁
  • 任何姿势下,文字与背景的对比度低于 4.5:1
  • 显示文字的字距设为 0,或计数器使用比例数字
  • 页面动作结束后仍有空闲姿势,或有「毫无理由地延长」的尾段
  • 导出中包含节拍器
  • 渲染中使用 CSS 过渡或动画,或库的 animate()
  • 未逐个姿势检查,并对照歌曲进行测量
  • 实时运行:声音在帧上播放,或前瞻调度未计入输出延迟
  • 实时运行:画面在拍点之后才绘制,或场景比其他内容晚一帧提交
  • 实时运行:首次按下启动声音时却将其静音,或未获许可便在减少动态效果模式下让场景移动

参考资料

打开当前步骤对应的文件。每份资料都将一个数值与其缘由对应起来,让这些信息延续到新歌曲中。

  • song.md:测量曲目、剪辑片段,并将其映射到姿势和小节。
  • clock.md:统一时钟、每个姿势的帧数、小节线、页面时长、Remotion 接线方式,以及 60fps 变体。
  • poses.md:定格动画间距,以及各类事物的规则。
  • hits.md:各元素的落点,以及转场。
  • skipping.md:何时跳过、保持、舍弃或弱化一个节拍,以及每种做法的原因。
  • sound.md:与采样对齐的咔嗒声、弱化处理、节拍音轨和节拍器。
  • type.md:画面中的字体,包括字距、字号、行距、对比度和逐字显示。
  • checking.md:联系表、测量同步、以时间戳记录备注,以及审查循环。
  • live.md:现场播放;在浏览器中使用节拍器并遵循相同规则:页面时钟、提前安排声音、启动和暂停声音、减少动态效果,以及屏幕上的文字。

工具:

  • assets/remotion/beat.ts:时钟和节拍辅助函数(TEMPO、SPEED、ms、useBeat、moved、clicked、press、blink、曲线)。
  • assets/remotion/transitions.tsx:onTheBeat、inSteps、onQuarters、slideOver、pixels、throughShape。
  • scripts/find-tempo.mjs:从 WAV 文件中分析歌曲的速度、网格、漂移和小节。
  • scripts/duck.mjs:在咔嗒声周围静音或降低音乐音量。
  • scripts/make-metronome.mjs:用于检查的节拍音轨。

评论

支持 Markdown