制作纸片分层动画视频(paper-collage layered video):把画面拆成背景、后排、主角、 前景等独立图层,用 Codex imagegen 出素材、Remotion 用 React 代码控制入场动画与遮挡、 MiniMax 生成中文旁白与配乐、FFmpeg 抽帧验收,最终渲染 MP4。 触发场景:"做一条视频"、"做个动画视频"、"纸片风/剪纸风/拼贴风动画"、"分层动画"、 "历史题材短视频&a
paper-collage-video — 纸片分层动画流水线是一项面向实际任务的技能,主要用于多数人做"纸片风视频",只是把一张完整图片放大缩小加点镜头移动;那样能动,但没有;
它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;
若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
多数人做"纸片风视频",只是把一张完整图片放大缩小加点镜头移动。那样能动,但没有 层次。真正的分层动画,核心不是生成一张漂亮的图,而是让每一张图片承担明确的叙事 位置:背景、后排、主角、前景各自独立,按不同节奏运动。
本 skill 自带一个已验证可跑通的 Remotion 模板(template/),下面每条约束都是
实跑中撞出来的,不是理论。
永远不要把人物画进背景。 人物一旦和背景粘在一起,就再也不能独立飞入、缩放、 重新排序或单独给节奏。底板必须是"空场",人物必须是独立透明 PNG。
| 工具 | 作用 | 凭证 |
|---|---|---|
Codex imagegen (game-asset-gen/scripts/gen.py) |
生成底板与角色 | codex login,不需要 API key |
| Remotion | 图层、入场、遮挡、字幕、音轨、渲染 | — |
| MiniMax | 中文旁白 TTS + 背景音乐 | MINIMAX_API_KEY(放 .env) |
| game-asset | 本地音效(impact/whoosh/tick)、BGM | 音效无需 key |
| Python + Pillow | 按 alpha 裁切图层(必需) | — |
| FFmpeg / ffprobe | 时长、音轨、抽帧验收 | — |
不要用智谱 GLM-Image 做这个。 实测它在右下角强制烙"AI生成"水印,
watermark_enabled:false 被忽略——底板直接报废,抠图时水印还会被当成主体边界。
Codex imagegen 没有这个问题。
1. script.json 先定分镜和文案,在出任何图之前 2. 底板 无人物的空场景 gen.py 3. 角色 一人一张透明 PNG gen.py --ref 4. 裁切 trim_layers.py ← 必需,别跳 5. 静态排版 先摆好,不要动画 6. lint-layout 机器检查构图 7. 动画 按 role 错峰入场 8. 旁白 tts.mjs → 旁白长度决定镜头长度 9. sync ffprobe → 时长 + 字幕时间轴 10. 渲染 + 抽帧验收
先定镜头,因为同一个人物在全景和特写里的大小、位置、朝向都不同。先随便出素材, 后面必然返工。
本 skill 以软链方式挂在项目下(源仓库:~/code/my-skills/paper-collage-video,
GitHub lifecoder1988/my-skills),因此模板就在当前项目里:
cp -r .claude/skills/paper-collage-video/template <目标目录> cd <目标目录> && npm install echo "MINIMAX_API_KEY=..." > .env # 图片不需要 key,走 codex login
要在新项目里启用本 skill,照这行链过去即可(不装到全局 ~/.claude/skills):
mkdir -p <新项目>/.claude/skills ln -s ~/code/my-skills/paper-collage-video <新项目>/.claude/skills/paper-collage-video
编辑 src/script.json(唯一手写真源),然后:
GEN=~/.claude/skills/game-asset-gen/scripts/gen.py STYLE=$(cat prompts/_style.md) # 底板:1536x1024,无人物 python3 $GEN --timeout 900 --size 1536x1024 --prompt-text "$STYLE ..." --out public/assets/plates/01-wide-bg.png # 角色:透明,--ref 第一个角色锁风格 python3 $GEN --background transparent --size 1024x1024 --ref public/assets/layers/emperor.png --prompt-text "$STYLE ..." --out public/assets/layers/attendant.png python3 scripts/trim_layers.py public/assets/layers # 必需 node scripts/tts.mjs # 旁白 node scripts/sync-durations.mjs # → src/script.build.json node scripts/lint-layout.mjs # 构图体检 npm run start # Remotion Studio 逐帧预览 npm run render && node scripts/check.mjs # → out/final.mp4 + 抽帧
旁白想用自己的声音:先录一段 10s–5min 的干净人声(mp3/m4a/wav,<20MB),
克隆后 voice_id 会自动写进 src/script.json 的 voice 字段,再重跑 TTS:
node scripts/clone-voice.mjs assets/voice/my_voice.mp3 --id myvoice2026 node scripts/tts.mjs --force
注意:voice_id 必须字母开头、仅字母数字、≥8 位,否则 API 拒收;上传走
purpose=voice_clone 的 multipart,失败同样藏在 base_resp.status_code 里
(HTTP 仍 200)。
音效与 BGM:
game-asset audio sfx --preset hit --duration-ms 420 --out public/audio/sfx/impact.wav game-asset audio sfx --preset whoosh --duration-ms 320 --out public/audio/sfx/whoosh.wav game-asset audio sfx --preset click --duration-ms 140 --out public/audio/sfx/tick.wav game-asset audio sfx --preset powerup --duration-ms 900 --out public/audio/sfx/riser.wav game-asset audio bgm --prompt prompts/bgm.md --instrumental --out public/audio/bgm.mp3
尺寸和运动幅度同时编码叙事等级。人物不是平均分布,也不是一样大。
| role | 高度(1080画布) | 运动 | z | 音效 |
|---|---|---|---|---|
primary 主角 |
~680–820 | 位移最大、有落地感 | 最上 | impact |
secondary 配角 |
~400–520 | 从两侧进入、中等 | 中 | whoosh |
tertiary 后排 |
~330–380 | 几乎不动,不抢戏 | 最下 | tick |
背景只做 ~1% 慢速推镜。
baseline 是脚底 Y,不是顶部——同一镜头所有人共用一条地面线。后排 baseline
更小(在画面更高 = 更远)且更矮。height 定尺寸,不要用 width(见下)。delay):主角先出现 → 配角补充 → 群像完成,不要一次塞满。"flip": true 复用左右两侧,不用生成镜像版。prompts/_style.md 存一份共用艺术指导,每张图都要重复它——imagegen 是文生图,
风格全靠提示词复述来统一(--ref 再加一层保险)。
角色提示词必须同时锁死:时代服饰、明确朝向、完整人物不裁切头手脚、白色剪纸 描边、纯透明背景、只有一个人、无场景、无投影、无文字、无水印。
底板提示词要正向描述(见下面的坑)。
底板提示词堆强调式否定会让 imagegen 卡死超时。 写
absolutely NO people, NO human figures, NO characters 那版跑满 600s 一张没出;
改成正向描述为主("An empty courtyard...", "Deserted architecture and landscape
only. No text, no watermark.")后 3 分钟出图,且真的没画人。描述画面里有什么,
而不是反复强调没有什么。
trim_layers.py 是必需步骤,不是优化。 imagegen 返回的人物浮在 1024×1024 里,
留白比例完全随机——实测人物只占图宽 33%~84%、脚下留白 2%~6%。不裁切的话
height/width 缩放的是留白而不是人物,主次大小关系会被随机破坏,人物还会
各自悬空脱离地面线。
按高度定尺寸,不是宽度。 原文用"宽 650 / 宽 575",那只在素材比例一致时成立。
alpha 裁切后各角色比例从 0.35(瘦高文官)到 0.89(带龙椅的皇帝)不等:给瘦高人物
设 width: 575 算出的高度是 1344px,超出 1080 画布,头直接被切掉。高度才是
1080 画布真正约束的维度,也才是"这个人多大"的直觉含义。跪姿人物天然约为站姿的 60%。
真人题材:先核事实,再做片子。 涉及在世真人时,先 WebSearch 核实——用户给的 前提可能本身就是错的(实测遇到过:"做拳王离婚故事",而当事人根本没离婚)。数字只 采主流媒体口径,自媒体独家、互相打架的说法一律不用,涉事陈述带出处("据公开报道" "某某在节目里说")。不生成未成年人形象。另外:imagegen 不会拒绝点名真人,但也 画不出本人——给的是通用长相,所以"画得像本人"这条在本流水线里做不到,按角色 特征写提示词即可。
渲染前先跑 lint-layout.mjs。 渲染要几分钟,构图错误不该等渲完才发现。它检查:
头/脚出画、后排被主角完全吞掉(白生成)、主次大小倒挂、入场帧撞车、脚底压在字幕带
下面。上面每一条都是实际撞到过的。
等 gen.py 进程退出,不要等文件出现。 codex 先把原图写到磁盘,gen.py 随后才做
_force_size 后处理。如果你用 until [ -f x.png ] 之类的循环一看到文件就去 trim,
_force_size 会把你已经裁好的图再 resize() 成 --size 的尺寸——而且它
不保宽高比,人物直接被拉变形。实测踩过:wife.png 被压成 1024×1024 正方形,
lint 报 790x790 才发现。要等就等 job/进程结束。
同理,--size 只是"强制拉伸到这个尺寸",不是"按比例缩放"。底板给 1536x1024
(codex 本来就出这个比例)是安全的;给一个和出图比例不符的 --size 会挤压画面。
gen.py 超时会漏 codex 孤儿进程。 超时只杀直接子进程,codex 本体会继续在后台空转 抢资源。清理:
ps -o pid,etime,command -ax | grep "[c]odex exec" # macOS 的 ps 没有 etimes kill -9 <老进程 pid>
MiniMax 用 https://api.minimax.io(不是 api.minimax.chat / api.minimaxi.com,
那两个返回 invalid api key)。TTS 返回的音频是 hex 编码(不是 base64),且失败时
HTTP 仍是 200,真实状态在 base_resp.status_code 里。node scripts/tts.mjs --voices
可列出全部音色(332 个,中文搜 Chinese (Mandarin)_)。
出图很慢:每张 2–4 分钟,底板可能更久。丢后台并行跑,别串行等。
npm run start 逐帧预览 + check.mjs 抽帧,重点看:
check.mjs 会自动验证:分辨率/帧率、音轨是否存在、时长、是否静音
(mean_volume < -60dB 判死),并抽 5 帧供肉眼检查。
历史人物、知识科普、商业故事、人物关系、城市发展——只要画面能拆成"背景、主角、
配角、前景",流程完全一样。改 script.json 的文案与图层、换 prompts/_style.md
的艺术指导即可,Remotion 组件不用动。
相关专题
热门下载
相关下载
精品课程
共6课时 | 54.7万人学习
共89课时 | 134.1万人学习
共49课时 | 82.6万人学习