运动图形与标题设计
针对由形状、静态图像和文本构建的片段提供的配方。它们嵌入到 montagent 的构建步骤循环中;如果尚未加载 montagent,请先加载。
在规划之前,请根据片段类型打开相应的参考文件:
| 片段类型 | 打开文件 |
|---|---|
| 广告片头、产品/社交媒体广告、开场、片尾 | 广告片头结构 |
| Logo 揭示、Logo 短片、任何循环内容 | Logo 循环 |
| 预告片,或任何 slammed、glitch、闪烁、HUD 效果 | 预告片特效 |
对于新片段或重新设计,请在选择布局前阅读创意方向。它涵盖了 Montagent 原生环境中的视觉概念、构图和编排。对于具体的剪辑任务,只阅读相关配方,并保留现有的方向。
在节拍表上规划
在编写任何元素之前,先编写节拍表:将所有进入、颜色变化、剪切和退出事件整合到一个表格中,每个事件占据 montagent-craft 提供的一个节拍(包括其节奏、重拍、帧对齐,以及哪些事件应落在节拍上)。
使用一个生成器脚本从节拍表构建项目,这样重新调整时间只需编辑一行代码。
配方
每个配方提供的是起始范围,而非最终答案。每个配方都以一个检查步骤结束:加载 montagent-craft,然后在该步骤指示的位置进行检查,然后再继续。
弹出动画
一种带有小幅度过冲并最终稳定的进入动画。过冲体现在曲线上:使用一对关键帧,其贝塞尔曲线的 y 值超过 1,而不是使用三个关键帧。
{
"frame": {"width": 1920, "height": 1080}, "fps": 30, "background": "#101418", "duration": 2000, "output": "out/pop.mp4",
"tracks": [
{"name": "badge", "layer": 10, "elements": [
{"id": "badge", "type": "ellipse", "start": 500, "end": 2000, "x": 960, "y": [{"t": 500, "v": 580}, {"t": 900, "v": 540, "ease": [0.34, 1.56, 0.64, 1]}], "origin": "center", "width": 240, "height": 240, "fill": "#FF5A36", "scale": [{"t": 500, "v": [0.5, 0.5]}, {"t": 900, "v": [1.0, 1.0], "ease": [0.34, 1.56, 0.64, 1]}]}
]}
]
}
- 时长: 300–450 毫秒。较短的时长感觉更干脆,较长的则更漂浮。
- 起始缩放: 文本和卡片为 0.5–0.7;小形状和徽章为 0。
- 过冲:
[0.34, 1.56, 0.64, 1]峰值大约超过目标值的 10%。将第二个数字提高到 1.8 可获得更活泼的效果,或降低到 1.3 以获得更稳重的效果。在一个片段中,所有弹出动画保持使用同一个曲线。 - 上升:
y轴上 20–60 像素的移动,配合相同的曲线,可以增加重量感。对于排列在一行的弹出动画,可以跳过此步。 - 交错: 当每个项目落在自己的节拍上时,间隔一个节拍;如果项目在同一节拍内,则间隔 60–120 毫秒。
- 检查: 在开始时间加上动画时长 40% 的位置查看
frame,此时过冲达到峰值,并检查是否有任何元素超出帧边缘或与相邻元素重叠。然后preview一个从第一次弹出前到最后一次弹出后的时间范围。
保持,然后清除
进入动画的最后一个关键帧已经保持状态,因此保持本身不消耗额外性能。退出动画从保持值的时刻开始,用一个关键帧记录该值。validate 会将该关键帧报告为 R-EASE-INERT,说明文档会解释为何这是预期的。
- 退出时长: 200–350 毫秒,使用
ease-in。退出速度比进入快。 - 退出运动: 一个淡出加上 20–40 像素的位移,或缩放到 0.9。若要硬切,使用单个
step关键帧跳转到新值(请参考入门项目中的标题)。 - 保持时长: 由
montagent-craft提供的阅读时间。 - 分组退出: 整个组一起退出,或按照阅读顺序交错 30–60 毫秒。
- 检查: 在退出前的最后一帧查看
frame,确认整个组完全进入且处于静止状态。
单词上的颜色变化
使用该词所在行的一个 highlight 在节拍上改变其颜色。这会保持一个元素:无需创建第二个副本进行交叉淡入淡出,且元素自身的颜色仍然是整行的颜色。
{
"frame": {"width": 1920, "height": 1080}, "fps": 30, "background": "#101418", "duration": 3000, "output": "out/accent.mp4",
"fonts": {"title": [{"file": "fonts/Inter-Bold.ttf"}]},
"fontVendor": {"fonts/Inter-Bold.ttf": {"licence": "OFL-1.1", "source": "https://github.com/rsms/inter", "sha256": "288316099b1e0a47a4716d159098005eef7c0066921f34e3200393dbdb01947f"}},
"tracks": [
{"name": "line", "layer": 20, "elements": [
{"id": "line", "type": "text", "start": 0, "end": 3000, "x": 960, "y": 540, "origin": "center", "width": 1600, "height": 140, "font": "title", "size": 110, "color": "#F5F0E6", "align": "center", "runs": [{"text": "Video your agent can "}, {"text": "read.", "highlight": {"start": 2000, "end": 3000, "color": "#FF5A36"}}], "caption": false}
]}
]
}
- 时机: 变化发生在节拍上,绝不能发生在单词进入的时刻。在单词变化之前,至少留出半个节拍的时间让眼睛先阅读该词。
- 强调: 当单词是一个独立的元素时,在同一节拍上应用一个缩放凸起(从 1.0 到 1.06–1.1,再在 200–300 毫秒内恢复),以使变化更加突出。
- 检查: 在变化前和变化后各查看一帧
frame,确认只有目标单词发生了改变。
动态文本与键入效果
当每个字母、单词或行共享一个运动曲线并以规则步长(every 毫秒)出现时,保持单个 text 元素,并使用 units 进行交错(by:letter、word 或 line;order:forward、reverse、center_out 或 edges_in)。以下示例中,一个标题从中心向外模糊显现:
{
"frame": {"width": 1920, "height": 1080}, "fps": 30, "background": "#101418", "duration": 2000, "output": "out/evaporate.mp4",
"fonts": {"title": [{"file": "fonts/Inter-Bold.ttf"}]},
"fontVendor": {"fonts/Inter-Bold.ttf": {"licence": "OFL-1.1", "source": "https://github.com/rsms/inter", "sha256": "288316099b1e0a47a4716d159098005eef7c0066921f34e3200393dbdb01947f"}},
"tracks": [
{"name": "title", "layer": 20, "elements": [
{"id": "evaporate", "type": "text", "start": 0, "end": 2000, "x": 960, "y": 540, "origin": "center", "width": 1400, "height": 160, "font": "title", "size": 108, "color": "#F5F0E6", "align": "center", "runs": [{"text": "SHARP FOCUS"}], "units": {"by": "letter", "order": "center_out", "every": 45, "y": [{"t": 0, "v": 20}, {"t": 360, "v": 0, "ease": "ease-out"}], "opacity": [{"t": 0, "v": 0}, {"t": 360, "v": 1, "ease": "ease-out"}], "blur": [{"t": 0, "v": 24}, {"t": 360, "v": 0, "ease": "ease-out"}]}, "caption": false}
]}
]
}
- 蒸发/光学聚焦(
blur): 在units中设置blur从16–32像素降至0,时长 300–450 毫秒(ease-out),并与opacity(0 -> 1)和一个小幅度y提升(16–28像素-> 0)或scale([1.08, 1.08] -> [1.0, 1.0])组合。一旦blur达到0且opacity达到1,渲染器将采用快速路径,没有额外的图层开销。 - 对称顺序(
center_out、edges_in):center_out从中间单元(偶数时从中间两个单元)开始,逐步向两端扩展;edges_in从首尾单元同时开始,向中间汇聚。 - 行波(
A -> 峰值 -> A): 无需像 After Effects 的范围选择器(triangle、round或smooth),直接在units中编写一个三个关键帧的返回曲线——例如"y": [{"t": 0, "v": 0}, {"t": 180, "v": -28, "ease": "ease-out"}, {"t": 360, "v": 0, "ease": "ease-in-out"}](或scale/blur脉冲)——让every使峰值在整个行中波动。若要将某个单词或字母独立出来并设置自定义时间,可将其拆分为自己独立的run并附带unit。 - 保持字形为
text(切勿将字形转换为path):path元素只有一个轮廓(points,没有holes),因此将多轮廓字形(如O、A、B、8、i、?、%)转换为path会填充内部区域或丢失孤立部分(ADR-0224)。将文本保持为text,并通过stroke、units、path和effects进行样式化或动画处理。 - 不规则节拍时间或打字光标(
scripts/type_on.py): 当单词落在不规则的节拍时间戳上,或字母需要带有步进光标时,运行python3 scripts/type_on.py <project> <spec>(使用--help查看 spec)。它会使用measure测量行,并为每个单元发射一个text元素("by": "word"配合"enter": "pop"和times/highlights,或"by": "letter"在 60–150 毫秒内配合闪烁的cursor),并将每个标记为caption: false。 - 跟踪标题: 不要将标题拆分成字母。编写一个
text元素,并使用letter_spacing,单位为千分之一 em(每个字母后的像素距离为size × letter_spacing / 1000;负值收紧)。通过关键帧使标题逐渐散开:"letter_spacing": [{"t": 0, "v": 0}, {"t": 800, "v": 120, "ease": [0.65, 0, 0.35, 1]}]。任何非零值都会关闭可选的连字功能,像阿拉伯语这样的连字脚本不会产生间距。 - 垂直列和侧边文字: Montagent 没有
writing_mode属性(ADR-0224)。对于垂直堆叠的列(CJK 竖排文字或垂直字幕),使用\n连接字素("text": "東\n京\n都")放在一个狭窄的text框内,设置"align": "center"和"line_height": 1.0或1.1;将 2–3 个水平数字或拉丁首字母保持在一行("text": "第\n25\n回",纵中横排),并使用"by": "line"进行交错,使水平数字作为一个整体移动。对于侧边的拉丁文字,设置一个水平text元素,并使用"rotation": -90(从下往上读)或"rotation": 90(从上往下读)旋转它。 - 检查:
preview行出现的时间范围。然后在静止时frame检查,确保间距读起来像一行,单词或字母边界处没有过宽的间隙。
擦拭揭示
擦拭动画通过滑动一个边缘来揭示下方的物体。在被揭示物体的上方放置一个遮挡物:一个填充了与背景完全相同颜色的矩形。然后以远离边缘的方向缩小遮挡物,使其近边缘扫过物体。一条沿着该边缘移动的强调条能使擦拭动作看起来更刻意。
{
"frame": {"width": 1920, "height": 1080}, "fps": 30, "background": "#101418", "duration": 2000, "output": "out/wipe.mp4",
"tracks": [
{"name": "mark", "layer": 10, "elements": [
{"id": "mark", "type": "image", "start": 0, "end": 2000, "source": "media/still.png", "x": 960, "y": 540, "origin": "center", "width": 360, "height": 360, "fit": "contain"}
]},
{"name": "occluder", "layer": 11, "elements": [
{"id": "occluder", "type": "rect", "start": 0, "end": 1000, "x": 1160, "y": 540, "origin": "center-right", "width": 400, "height": 400, "fill": "#101418", "scale": [{"t": 400, "v": [1.0, 1.0]}, {"t": 900, "v": [0.0, 1.0], "ease": [0.65, 0, 0.35, 1]}]}
]},
{"name": "edge", "layer": 12, "elements": [
{"id": "edge", "type": "rect", "start": 400, "end": 1000, "x": [{"t": 400, "v": 760}, {"t": 900, "v": 1160, "ease": [0.65, 0, 0.35, 1]}], "y": 540, "origin": "center", "width": 16, "height": 420, "fill": "#FF5A36", "opacity": [{"t": 900, "v": 1.0}, {"t": 1000, "v": 0.0, "ease": "ease-in"}]}
]}
]
}
- 时长: 400–600 毫秒,
[0.65, 0, 0.35, 1]:一个起始和结束都柔和的扫动。 - 遮挡物: 在物体的每一侧都大出 20–40 像素,并使用与其背后的地面相同的填充。起始关键帧放置在擦拭开始的位置,以便物体在此之前保持被覆盖状态。
- 边缘条: 8–24 像素宽,比遮挡物高 5–10%,沿相同的曲线和时间移动。它在扫动完成后 100–200 毫秒内消失。
- 方向: 沿阅读方向擦拭;要擦除,则从近边缘反向扩大遮挡物。
- 复杂背景: 遮挡物仅在纯色背景上有效。在照片、渐变或视频内容上,应为元素自身的
mask设置关键帧:{"name": "mask", "shape": "rect", "x": 0, "y": 0, "width": [{"t": 400, "v": 0}, {"t": 900, "v": 400, "ease": [0.65, 0, 0.35, 1]}], "height": 400}可以从左边缘揭示一个 400×400 的元素,无需遮挡物。矩形蒙版是元素本地的,相对于元素的左上角;宽度为零的蒙版会隐藏元素,因此揭示开始时是覆盖的。添加feather以获得柔边。 - 检查: 在扫动进行到一半时
frame检查,确保边缘条恰好覆盖遮挡物的边缘;然后在扫动结束后立即frame检查,确认遮挡物已完全消失。
通过推、滑或擦拭进行转场
为了从一个卡片、静态图像或场景平滑移动到下一个,使用 transition 类型的 push、slide 或 wipe。对两个元素分别设置 x 关键帧会导致在连接处产生偏移;过渡效果确保两半像素级对齐,validate 会检查这一点。
{
"frame": {"width": 1920, "height": 1080}, "fps": 30, "background": "#101418", "duration": 3000, "output": "out/handover.mp4",
"tracks": [
{"name": "first", "layer": 10, "elements": [
{"id": "card-1", "type": "rect", "start": 0, "end": 1700, "x": 0, "y": 0, "origin": "top-left", "width": 1920, "height": 1080, "fill": "#FF5A36"}
]},
{"name": "second", "layer": 11, "elements": [
{"id": "card-2", "type": "rect", "start": 1200, "end": 3000, "x": 0, "y": 0, "origin": "top-left", "width": 1920, "height": 1080, "fill": "#2E86FF"}
]},
{"name": "handover", "layer": 12, "elements": [
{"id": "handover", "type": "transition", "start": 1200, "end": 1700, "kind": "push", "from": "card-1", "to": "card-2", "direction": "left", "ease": [0.65, 0, 0.35, 1]}
]}
]
}
- 类型: 当两个元素属于一个序列(页面、步骤、轮播)时使用
push;当新元素落在静止的旧元素之上时使用slide;当旧元素应保持在原位并被划掉时使用wipe。 - 方向: 运动传播的方向。
left使新元素从右侧进入,符合阅读方向。 - 时长: 400–600 毫秒,
[0.65, 0, 0.35, 1]。过渡的start和end正好是两个元素共存的时段。 - 滑动层级: 在
slide中,进入的元素必须位于更高的layer上。 - 检查: 在过渡的中段
frame检查,确保连接处没有间隙或重叠。然后preview整个过渡范围,确认运动流畅。