动画风格
一个包含 39 种手工构建动画风格的 Remotion 项目。每种风格都重新绘制同一个固定故事,以便横向对比并作为模板复用:水壶进入画面,倾斜,将茶倒入陶杯(kulhad),杯子满起,水壶离开,蒸汽升起。 150 帧,30 fps,1080×1350 (4:5),静默。以 --scale=2 渲染时为 2160×2700。
所有内容均由代码绘制:使用带有滤镜的 SVG,或每帧绘制的 <canvas>。不涉及任何图像模型、视频模型或库存素材,因此输出是原创且可重复的。
项目位于此技能的文件夹中(本文档所在的文件夹,下文称为 <skill>)。请在此文件夹中运行命令。
0. 会话中的首次操作
cd <skill>并检查node_modules/是否存在。如果不存在,运行npm install(大约一分钟,需要 Node 18+)。npm run list会打印所有 39 种风格及其编号、slug 和一行预览。- 首次渲染会下载一个无头 Chrome(约 100 MB)。需要互联网连接。
通常的请求:“制作一个 5 秒的 <想法> 视频,使用 <风格> 风格”
这是主要任务(也是引导用户来到这里的问题)。从头到尾执行,不要中途提问,除非想法不明确:
- 工作文件夹: 使用用户当前工作目录下的
animation-films/。如果该文件夹不存在,则从当前目录通过命令node <skill>/scripts/new-project.mjs animation-films创建,然后执行cd animation-films && npm install(首次之后很快)。如果已存在(后续操作),则直接复用。 - 选择风格 从请求中确定(第 1 节)。如果没有指明风格名称,则推荐三个适合该想法并询问用户。
- 规划故事 在短表格中涵盖六个节拍(第 3 节)。在一个消息中展示给用户并继续;除非有歧义,否则不要等待。
- 先定义形状: 将用户对象(角色、容器、流动物、上升物)放入
src/films/<idea>-shapes.ts,作为与story.ts中P相同坐标系的 SVG 路径,顶部以节拍表格作为注释。此想法的每种风格版本都会从此文件导入。 - 构建
src/films/<idea>-<style>.tsx,基于风格的组件(第 3 节),使用形状文件,并在src/films/index.ts中以 id<idea>-<style>注册它。 - 检查: 运行
npm run render -- <idea>-<style> --frames并查看所有六个帧(第 5 节)。修复并重复。 - 交付: 运行
npm run render -- <idea>-<style> --final并将out/<idea>-<style>.mp4的完整路径提供给用户。告知用户他们可以要求另一种风格(“现在做成像素艺术”)。
重做风格的后续请求(“现在做成像素艺术”、“试试水彩”、“同样做成木刻”): 保持在同一个 animation-films/ 文件夹内,使用相同的想法、节拍表格和形状文件,基于新风格的组件构建新的 src/films/<idea>-<new-style>.tsx。与之前的版本注册在一起(切勿覆盖),检查帧,渲染最终版本并提供新路径。如果用户表述模糊(“8-bit”、“老电影”、“粉笔”),请参照第 1 节进行映射。3D 外观风格(57、58、59、62、80)将对象构建为 3D 网格,因此在这些风格中替换新对象需要更多工作:使用 src/styles/3d/mesh.ts 中的辅助函数将形状转换为简单的车削或立方体网格。
1. 选择风格
如果用户尚未说明,询问他们想要哪种风格。如果他们不确定,显示按家族分组的 npm run list 以及 README.md 中的图库(每种风格在 media/thumbs/ 中有动态预览,在 media/videos/ 中有 720×900 MP4)。宽松匹配:“Japanese print”→ 33 Ukiyo-e,“chalk”→ 02 Chalkboard,“8-bit”或“retro game”→ 64 Pixel art RPG,“clay”→ 62 Claymation,“Lego-like”→ 58 Brick toy(切勿在屏幕上称其为 Lego),“old movie”→ 70 Silent film 或 71 Newsreel,“sci-fi screen”→ 84 Hologram HUD。
| 家族 | 风格(编号, slug) |
|---|---|
| 在黑板上绘制 | 01 whiteboard · 02 chalkboard · 05 notepad-doodle · 06 blueprint |
| 手绘与涂画 | 08 hand-drawn · 09 urban-sketch · 10 crayon-book · 11 watercolour · 13 one-line · 16 cel-line-boil |
| 卡通 | 17 flat-vector · 21 cel-anime-80s · 22 stick-figure |
| 纸张与玩偶 | 23 cut-out · 24 red-papercut · 25 paper-pop-up · 27 shadow-puppet |
| 印刷与工艺 | 28 woodcut · 29 engraving · 31 silkscreen-poster · 33 ukiyo-e · 34 art-deco · 35 stained-glass |
| 图形与讲解 | 39 memphis-diorama · 51 sticker-explainer · 52 math-diagram |
| 3D 外观 | 57 low-poly-island · 58 brick-toy · 59 glass-product · 62 claymation · 80 lidar-survey |
| 电影时代 | 70 silent-film · 71 newsreel |
| 屏幕与科学 | 64 pixel-art-rpg · 81 microscope · 84 hologram-hud · 85 circuit-board |
| 抽象 | 87 liquid-motion · 89 generative-abstract |
src/catalog.ts 是单一列表:包含编号、组合 id (33-ukiyo-e)、名称、家族、外观、风格屏幕上显示的文字,以及其代码所在的文件。
2. 按原样展示内置风格
npm run render -- 33 # 720x900 预览 -> out/33-ukiyo-e_preview.mp4 (几秒到一分钟)
npm run render -- 33 --final # 2160x2700 主版 -> out/33-ukiyo-e.mp4 (crf 16)
npm run render -- 33 --frames # 6 个检查帧 -> out/33-ukiyo-e_frames/ (如果安装了 ffmpeg 则生成联系表)
npm run render -- 33 --still 75 # 第 75 帧的单个 PNG
风格可以通过编号、id、slug 或其名称的一部分(ukiyo、pixel、stained)来指定。现成的 720×900 视频已存在于 media/videos/ 中,完整的 2160×2700 主版在 GitHub 发布页面上,因此要展示一种风格,完全不需要渲染。
3. 用用户的故事或对象重新讲述
故事固定在 src/story.ts 中。保持其时间线,改变绘制的内容。这就是让一部新电影首次就能正确呈现的原因:每种风格已经知道如何编排这些节拍。
节拍(30 fps 的帧,对于 5 秒电影切勿移动它们):
| 帧 | 节拍 | storyAt(f) 返回的值 |
|---|---|---|
| 0–24 | 角色从右侧进入 | kx, ky |
| 24–42 | 角色就位(倾斜) | tilt (0 → −38°) |
| 42–96 | 主要动作(倒出) | streaming, stream(从 tip 到杯中的 SVG 路径) |
| 44–100 | 结果逐渐显现(杯子满起) | level 0 → 1, surface(茶的椭圆) |
| 96–126 | 结束并离开 | tilt 回到 0, kx 滑出 |
| 104–150 | 后续效果(蒸汽升起) | steam[0..2] 0 → 1, 使用 steamPath(i, p, t0) 绘制 |
story.ts 中的 P 保存了杯子(kulhadBody、kulhadOutline、rim)和水壶(kettleBody、kettleLid、kettleSpout、kettleHandle、kettleBand,以及 KNOB)的 SVG 路径。水壶围绕其自身原点绘制,并通过 translate(kx, ky) rotate(tilt) 放置。
首先将用户的故事映射到相同的六个节拍,使用一个短表格,并展示给用户。示例:
| 用户的想法 | 角色(水壶) | 动作(倒出) | 结果(填充) | 后续效果(蒸汽) |
|---|---|---|---|---|
| 咖啡倒入马克杯 | 咖啡壶 | 咖啡流 | 马克杯满起 | 蒸汽 |
| 给植物浇水 | 喷壶 | 水滴 | 土壤变暗,幼苗随 level 生长 | 两片叶子展开 |
| 存钱 | 握着硬币的手 | 硬币落下 | 存钱罐满起(level→ 硬币堆叠) | 闪光 |
| 油漆倒入罐子 | 油漆罐 | 厚油漆 | 罐子满起 | 一滴油漆沉淀 |
| 数据上传(抽象或信息风格) | 一朵云 | 数据包沿 stream 行进 | 条形图或计量表随 level 填充 | 一个对勾标记升起 |
然后构建它:
- 在单独的项目中工作,以保持安装的技能干净:默认情况下在用户当前文件夹的
animation-films/中(参见“通常的请求”),通过node <skill>/scripts/new-project.mjs <folder>创建,然后在其内部执行npm install。切勿在<skill>内部构建用户的电影。 - 将所选风格的组件从其文件(参见
src/catalog.ts)复制到src/films/<film-name>.tsx,同时复制其导入的辅助函数。切勿就地编辑 39 个原始文件:它们是参考标准。 - 替换形状,而不是时间线:将水壶和杯子的路径替换为用户对象的路径(保持大致相同的大小和位置:杯子居中于 x 540,边缘在 y ≈ 800;角色静止时大约在 (796, 324),其“壶嘴”在局部点 (−251, −62)),重新着色液体,改变上升物。保持风格自身的材质、调色板逻辑、滤镜、纹理和运动特征(手绘媒体使用双帧或三帧步进,数字媒体平滑,胶片媒体闪烁)。
- 如果风格显示文字(catalog 的
text字段中列出:51、52、64、70、71、84 和 85 显示文字),为新的故事重写它们:简短、简洁的英文,仅关于故事本身。 - 在
src/films/index.ts中注册电影并渲染(第 4 节)。
更长或不同的故事(更多节拍、两个角色、摄像机运动):在 story.ts 旁边编写一个新的故事文件(例如 src/films/my-story.ts),包含它自己的 storyAt(f),返回绘图所需的所有值,在其顶部以表格形式列出新的节拍,在电影上设置 durationInFrames,并遵循规则“故事文件拥有所有时间线,风格文件拥有所有外观”。仅在风格意在呈现手绘效果时对帧进行量化(Math.floor(f / 2) * 2)。
代码规则:只能确定性(无 Math.random()、无 Date;使用帧或索引的种子函数),唯一滤镜和 clipPath id,字体仅通过 @remotion/google-fonts/<Font> 并使用模块级别的 loadFont(),除了 package.json 外不安装其他内容。
4. 渲染
| 需求 | 命令 |
|---|---|
| 快速预览 | npm run render -- <film-or-style>(720×900, crf 23) |
| 最终版 | npm run render -- <film-or-style> --final(2160×2700, crf 16) |
src/films 中的电影 | npm run render -- <film-id> --final(相同的标志也适用:--frames、--still 75) |
| 实时编辑 | npm run studio 在浏览器中打开 Remotion Studio |
3D 外观风格(57、58、59、62、80)在 1.5 倍(80 风格为 1.25 倍)1080 宽的画布上绘制软件渲染场景,然后拉伸到 2160,因此它们比其他风格稍微柔和和慢一些。为了获得更清晰的最终版本,提高 src/catalog.ts 中 glView(...) 的第二个参数(2 = 原生 4K,速度大约慢一倍)。
5. 在交付前检查输出
运行 npm run render -- <id> --frames 并查看每一帧(阅读 PNG)。然后修复并重新渲染。允许两到三次迭代。
- 可读的故事: 在第 50 帧时显然有物体在倒出,在 75 帧时容器明显在填充,在 149 帧时后续效果可见。
- 无裁剪 在帧边缘;角色完全进入并完全离开。
- 分层: 液体保持在容器内部,流束进入容器(而不是在近边缘前面),蒸汽从表面升起。
- 风格从一帧即可辨认: 熟悉该风格的人应能通过一帧静态画面认出它。两种风格绝不应看起来像是彼此重新着色。
- 对比度: 液体和流束在背景上清晰可见,没有模糊不清。
- 运动特征: 手绘媒体使用步进,数字媒体平滑;没有不想要的闪烁。
- 文字: 只有你意图中的词语,拼写正确,在帧内。
- 内容规则(下文)全部通过。 最后从头到尾观看一次预览 MP4,然后渲染最终版本。
6. 内容规则(始终遵守)
- 无真实品牌、标志、产品、人物、频道名称或电视网络,屏幕上无商标名称。“积木玩具”是通用的玩具积木,绝非指名品牌。
- 无真实地图、地点或国界,无旗帜、政治符号或标语。如果有风景,则必须是虚构的,没有可识别的地理特征。
- 不得复制特定电影、节目、标题序列、游戏、角色或已知角色的轮廓或精灵图。 风格或流派可以接受;特定现有作品不行。
- 不得编造事实: 无虚假统计数据、价格、日期、引用或来源。数据风格仅绘制故事自身的值(如填充级别),不绘制其他内容。
- 仅在风格为排版、广播或游戏 UI 时显示文字,并且内容必须是关于故事本身的简短英文。
- 保持原创性:用户的故事,你自己的绘图,风格作为一种技术表现。
文件
| 路径 | 内容 |
|---|---|
src/story.ts | 故事:尺寸、时间线、节拍、对象路径、storyAt(f) 和 steamPath() |
src/kit.tsx | 共享辅助函数:Stage(SVG 舞台)、Paper(纸张纹理)、Rough(抖动位移)、region |
src/catalog.ts | 39 种风格:编号、id、名称、家族、外观、屏幕文字、文件、组件 |
src/styles/<family>/ | 按技术分组的绘制代码 |
src/films/ | 用户自己的电影 |
src/Root.tsx、src/index.ts | 将每种风格和电影注册为 Remotion 组合 |
scripts/render.mjs | npm run list / npm run render |
scripts/new-project.mjs | npm run new -- <folder> 将代码复制到一个新项目 |
media/videos/、media/thumbs/ | 每种风格的 720×900 MP4 和动态 WebP |