AnimationSpec v2 规范文档 — 自定义"文字动作"
本文档译自日语原版。日语版为正式版本,如有出入以日语版为准。
本文档是 movlyric 文字动作(歌词动画)定义“AnimationSpec v2”的完整规范。 在应用的“样式编辑器 →“动作”标签 → 文字动作 →“自定义”→ 用 AI 生成…”中使用(在背景绘制图形的“背景特效”是另一套体系, 其规范请见 GRAPHIC_SPEC(背景特效))。 以 v1(仅含动效)编写的 JSON 在 v2 中依然有效。v2 新增了颜色、描边、发光以及分轴缩放。 本文档的目标是:只读过此文档的 AI(或人类)也能写出可以直接粘贴到应用中并被接受的 JSON。 文档末尾附有可交给 AI 使用的提示词模板。将本文档整体作为提示词的一部分贴入同样可以正常使用。
1. 概述
movlyric 是一个按时间在画面上显示歌词(乐句)的视频引擎。 一句歌词按以下三个阶段运动。
- 入场(in):从乐句显示窗口的开头(乐句开始发声前约 1 秒)起,文字/单词出现的动作
- 显示中(稳定态):入场结束后的静止状态。可选叠加 activePulse(正弦波脉动)
- 退场(out):向显示窗口末尾(乐句发声结束后约 1 秒)消失的动作
AnimationSpec 用基于关键帧(keyframe)的声明式 JSON 描述 in / out / activePulse 这三部分。 不能写代码或表达式,只能使用以下规范中列出的字段。
2. JSON 格式完整规范
2.1 根节点:AnimationSpec
{ "in": <Phase>, "out": <Phase>, "activePulse": <Pulse> }
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
in |
Phase | 必需 | 入场阶段 |
out |
Phase | 必需 | 退场阶段(即使不需要退场演出也不能省略。想要无变化,请参见 §2.7) |
activePulse |
Pulse | 可选 | 显示中的脉动。不需要时整个键省略(不能写 null) |
根节点下能写的键只有这三个。
2.2 Phase(in / out 通用)
| 字段 | 类型 | 必需 | 取值范围 | 说明 |
|---|---|---|---|---|
durationMs |
整数 | 必需 | 50~4000 | 单个单元(文字/单词)该阶段所需时间(毫秒)。不可为小数 |
easing |
字符串 | 必需 | §3 中 31 种之一 | 对整个阶段应用一次的缓动(不能按 keyframe 区间分别指定) |
stagger |
Stagger | 必需 | — | 单元之间的时间差。即使不需要时间差,也要像 {"unit":"none","delayMs":0,"from":"start"} 这样三个字段全部必填 |
keyframes |
Keyframe[] | 必需 | 2~8 个 | 动作序列。t 须在 0..1 范围内严格递增(不允许相同值),首个必须 t=0,末个必须 t=1 |
2.3 Keyframe
| 字段 | 类型 | 必需 | 取值范围 | 含义、单位 |
|---|---|---|---|---|
t |
数值 | 必需 | 0~1 | 阶段内的归一化时间(0=开始,1=结束) |
alpha |
数值 | 可选 | 0~1 | 不透明度(0=透明,1=不透明) |
scale |
数值 | 可选 | 0.1~3 | 缩放倍率(1=原大小) |
dx |
数值 | 可选 | -2~2 | 横向偏移。单位是相对字号(fontSize)的比例(1.0=一个字符的宽度)。正值=向右 |
dy |
数值 | 可选 | -2~2 | 纵向偏移。单位是相对字号的比例。正值=向下(例如:从下方入场是正值→0) |
rotate |
数值 | 可选 | -360~360 | 旋转。单位是度。正值=顺时针 |
scaleX |
数值 | 可选 | 0.1~3 | 仅横向的缩放倍率,与 scale 相乘 |
scaleY |
数值 | 可选 | 0.1~3 | 仅纵向的缩放倍率,与 scale 相乘 |
tint |
整数 | 可选 | 0~16777215 | 文字颜色(0xRRGGBB 的十进制或十六进制表示) |
strokeWidth |
数值 | 可选 | 0~20 | 描边粗细(px)。0 表示无描边 |
strokeColor |
整数 | 可选 | 0~16777215 | 描边颜色 |
glow |
数值 | 可选 | 0~40 | 发光强度(模糊半径 px)。0 表示无发光 |
glowColor |
整数 | 可选 | 0~16777215 | 发光颜色。省略时以文字颜色发光(自发光) |
可写的属性只有上面这 13 个。不存在其他名称(如 x、y、opacity、color、blur、shadow 等)。
单位(dx/dy=相对字号的比例,rotate=度)与应用内动画编辑器显示单位(横/纵=相对字号的比例,旋转=度)一致。
"未指定"的含义因属性而异。 弄错这一点会导致外观不符合预期。
| 属性 | 未指定时 |
|---|---|
alpha / scale / scaleX / scaleY |
1(无变化) |
dx / dy / rotate |
0(无变化) |
tint |
不改变文字颜色(样式的卡拉OK发声变色效果照常生效) |
strokeWidth / strokeColor |
沿用样式本身的描边(不会变成 0) |
glow / glowColor |
无发光 |
也就是说,如果不想改变颜色或描边,请不要写对应的属性。一旦写了 tint,该文字就不再随演唱进行变色(未发声→已发声),而是固定为指定颜色。
分别改变 scaleX 和 scaleY,可以做出挤压拉伸(squash & stretch)的效果。在弹跳、落地等表现中加入这种效果会让动作更柔和。
2.4 Stagger(时间差)
| 字段 | 类型 | 必需 | 取值范围 | 说明 |
|---|---|---|---|---|
unit |
字符串 | 必需 | "none" / "char" / "word" |
时间差的单位。none=整句同时,char=按字符逐个,word=按单词逐个 |
delayMs |
整数 | 必需 | 0~500 | 每个单元的延迟(毫秒)。不可为小数。即使 unit 为 none 也要明确写 0 |
from |
字符串 | 必需 | "start" / "end" / "center" |
起点。start=从开头,end=从末尾,center=从中央向两侧(个数为偶数时,中间两个同时最先动) |
第 n 个动作的单元,其延迟为 顺序 × delayMs。注音(振假名)始终与所属单词保持相同相位运动。
in 和 out 可以分别指定不同的 stagger(例如:入场从开头开始,退场从末尾开始)。
2.5 Pulse(activePulse,可选)
| 字段 | 类型 | 必需 | 取值范围 | 说明 |
|---|---|---|---|---|
property |
字符串 | 必需 | "scale" / "alpha" |
脉动作用的对象 |
amplitude |
数值 | 必需 | 0~0.5 | 摆动幅度 |
periodMs |
整数 | 必需 | 200~4000 | 周期(毫秒)。不可为小数 |
行为:仅在每个单元完成入场之后、开始退场之前,会将 1 + amplitude × sin(2π × nowMs / periodMs) 乘到该 property 上。
nowMs 是视频的绝对时间,因此脉动的相位会跨乐句连续(具有确定性 = 预览与导出结果一致)。
注意:当 property:"alpha" 且稳定态 alpha 为 1 时,超过 1 的半个周期会被截断,因此只会看到"变暗"的一侧(作为明灭效果这是自然的)。
2.6 插值语义(稀疏 keyframe)
- 每个属性各自独立插值。某个属性只会把“拥有该属性的 keyframe”连起来做线性插值。
- 例如:
alpha只写在 t=0 和 t=1,scale写在 t=0 / 0.5 / 1,像这样稀疏地书写也是可以的。
- 例如:
- 在拥有该属性的第一个 keyframe 之前,保持第一个值;在最后一个 keyframe 之后,保持最后一个值(最近邻,不做外插)。
- 任何 keyframe 都未写的属性,按 §2.3“未指定时”的表处理。动效类属性的默认值是恒等值(alpha=1, scale=1, scaleX=1, scaleY=1, dx=0, dy=0, rotate=0),但颜色、描边则是"不覆盖"(保留样式原本的值),这一点不同。
- easing 只对"阶段进度 0→1"应用一次,所有属性都用该缓动后的值来插值。
2.7 in 与 out 的合成、稳定态
in 与 out 始终同时求值,合成方式为:alpha 与 scale 相乘,dx / dy / rotate 相加。因此:
- in 的 t=1 = 显示中的稳定态。通常应设为恒等值(alpha 1, scale 1, dx 0, dy 0, rotate 0)。
- out 的 t=0 必须是恒等值。否则显示中的外观会持续被 out 的值带偏。
- 如果不需要退场演出,可以把 out 设为"无变化":把
keyframes写成[{"t":0,"alpha":1},{"t":1,"alpha":1}]这样相同的值即可保持静止(注意 t 仍需保持升序)。
合成后的 alpha 最终会被截断到 0..1 范围内。
2.8 时间轴与短乐句的压缩
- 显示窗口在乐句发声区间的前后各留有约 1000ms 的余量。in 从窗口开头开始;out 则反向推算,使"最后一个单元的 t=1 恰好落在窗口末尾"。
- 整个阶段所需的总时长是
durationMs + 最大 stagger 延迟(最大顺序 × delayMs)。当它比显示窗口更长时,整个时间轴会被等比压缩(这样即使窗口较短,文字也不会一直不出现)。歌词字数多、charstagger 使用较大 delayMs 时容易被压缩,建议让durationMs + 字数 × delayMs控制在 2000ms 左右。
3. 缓动函数一览(31 种)
easing 可以填写的名称仅限以下这 31 种(大小写也须严格匹配。ease-in、easeOutBack 等其他写法均不可用)。
| 名称 | 说明 |
|---|---|
linear |
匀速 |
inQuad / outQuad / inOutQuad |
二次方。平缓的加速/减速/两者兼有 |
inCubic / outCubic / inOutCubic |
三次方。标准的加速/减速/两者兼有 |
inQuart / outQuart / inOutQuart |
四次方。较强的加速/减速/两者兼有 |
inQuint / outQuint / inOutQuint |
五次方。相当强的加速/减速/两者兼有 |
inSine / outSine / inOutSine |
正弦波。最柔和的加速/减速/两者兼有 |
inExpo / outExpo / inOutExpo |
指数。剧烈的加速/减速/两者兼有 |
inCirc / outCirc / inOutCirc |
圆弧。末端急促的加速/减速/两者兼有 |
inBack / outBack / inOutBack |
冲过头再弹回(反冲) |
inElastic / outElastic / inOutElastic |
类似弹簧的振动 |
inBounce / outBounce / inOutBounce |
类似球体弹跳的反弹 |
重要(引擎的实际行为):back / elastic 系列在过程中进度会超出 0..1 的范围,但由于插值采用最近邻截断,输出会停留在端点 keyframe 的值(不会超出 t=1 处的值)。
如果想确保出现"冲过头再弹回"的效果,请在中间 keyframe 中明确写出(例如把 scale 写成 0 → 1.15 → 1)。
4. 常见错误(禁止事项)
以下写法会导致校验错误或产生意料之外的外观。请务必避免。
- 超出取值范围:
scale: 0、3.5,alpha: 1.2,dx: 2.5,durationMs: 30或5000,delayMs: 600,amplitude: 0.8,periodMs: 100等均不允许。请严格遵循 §2 的取值范围表 - 不存在的属性:keyframe 中只能写 §2.3 的这 13 个(
t, alpha, scale, scaleX, scaleY, dx, dy, rotate, tint, strokeWidth, strokeColor, glow, glowColor)。x、y、opacity、color、blur、skew、shadow等均不可。也不能按 keyframe 单独指定 easing - 用非整数方式写颜色:
"#ff0066"、"red"均不可。请以整数写0xRRGGBB(十六进制字面量0xff0066或十进制16711782) - 把不想改变的属性写成 0:
strokeWidth: 0表示"取消描边"。想保留样式的描边,就不要写这个属性 - t 的顺序错误:不允许 t 降序或重复相同值(须严格升序)。别忘了首个 t=0、末个 t=1
- keyframes 的数量:1 个以下或 9 个以上均不可(须为 2~8 个)
- 禁止使用小数的位置:
durationMs/delayMs/periodMs只能是整数 - easing 名称写法不一致:须与 §3 中的 31 种严格一致
- 省略 in / out:两者均为必需项。stagger 的三个字段(unit / delayMs / from)也始终必需
- out 的 t=0 不是恒等值:会导致显示中的外观发生偏移(§2.7)
- 混入非 JSON 内容:注释、末尾逗号、表达式(如
"dy": "0.5 * 2")均不可。所有值必须是数值字面量或允许的字符串
5. 示例
以下三个都是可以直接粘贴到应用中并通过校验的完整 JSON。
5.1 轻柔浮现
文字从稍下方柔和地淡入,退场时向上飘出并淡出。
{
"in": {
"durationMs": 700,
"easing": "outSine",
"stagger": { "unit": "char", "delayMs": 40, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 0, "dy": 0.4 },
{ "t": 1, "alpha": 1, "dy": 0 }
]
},
"out": {
"durationMs": 600,
"easing": "inSine",
"stagger": { "unit": "char", "delayMs": 30, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 1, "dy": 0 },
{ "t": 1, "alpha": 0, "dy": -0.3 }
]
}
}
5.2 文字旋转弹跳登场
从略微旋转的状态开始,通过中间 keyframe 明确写出冲过头(scale 1.15)的效果,做出弹跳入场。退场时从末尾文字开始反向旋转并缩小。
alpha 是稀疏指定的示例:in 中在 t=0.6 前就达到 1,之后保持末值 1。
{
"in": {
"durationMs": 800,
"easing": "outBack",
"stagger": { "unit": "char", "delayMs": 60, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 0, "scale": 0.3, "rotate": -180 },
{ "t": 0.6, "alpha": 1, "scale": 1.15, "rotate": 20 },
{ "t": 1, "scale": 1, "rotate": 0 }
]
},
"out": {
"durationMs": 500,
"easing": "inBack",
"stagger": { "unit": "char", "delayMs": 40, "from": "end" },
"keyframes": [
{ "t": 0, "alpha": 1, "scale": 1, "rotate": 0 },
{ "t": 1, "alpha": 0, "scale": 0.3, "rotate": 180 }
]
}
}
5.3 逐词从下方滑入 + 显示中缓慢明灭
以单词为单位的 stagger 从下方依次出现,显示中通过 activePulse 缓慢明灭。退场时整句同时淡出。
{
"in": {
"durationMs": 600,
"easing": "outCubic",
"stagger": { "unit": "word", "delayMs": 120, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 0, "dy": 0.8 },
{ "t": 1, "alpha": 1, "dy": 0 }
]
},
"out": {
"durationMs": 400,
"easing": "inQuad",
"stagger": { "unit": "none", "delayMs": 0, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 1 },
{ "t": 1, "alpha": 0 }
]
},
"activePulse": { "property": "alpha", "amplitude": 0.25, "periodMs": 1600 }
}
5.4 如霓虹灯般发光登场
用 glow 制造发光,用 glowColor 指定光的颜色。退场时让发光收敛到 0。由于没有写 tint,文字颜色仍会随演唱正常变色。
{
"in": {
"durationMs": 500,
"easing": "outCubic",
"stagger": { "unit": "char", "delayMs": 40, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 0, "glow": 0, "glowColor": 65535 },
{ "t": 0.6, "alpha": 1, "glow": 34, "glowColor": 65535 },
{ "t": 1, "alpha": 1, "glow": 20, "glowColor": 65535 }
]
},
"out": {
"durationMs": 400,
"easing": "inQuad",
"stagger": { "unit": "none", "delayMs": 0, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 1, "glow": 20, "glowColor": 65535 },
{ "t": 1, "alpha": 0, "glow": 0, "glowColor": 65535 }
]
}
}
5.5 变色出现
改变 tint 会让文字颜色随之插值。写了 tint 的乐句不会再随演唱变色,因此颜色需要全部由这份 spec 决定。
{
"in": {
"durationMs": 700,
"easing": "outQuad",
"stagger": { "unit": "char", "delayMs": 30, "from": "center" },
"keyframes": [
{ "t": 0, "alpha": 0, "tint": 16711808 },
{ "t": 1, "alpha": 1, "tint": 16777215 }
]
},
"out": {
"durationMs": 400,
"easing": "inQuad",
"stagger": { "unit": "none", "delayMs": 0, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 1 },
{ "t": 1, "alpha": 0 }
]
}
}
5.6 挤压拉伸落地(squash & stretch)
让 scaleX 与 scaleY 反方向变化。下落过程中呈纵向拉长,落地瞬间横向挤扁,然后再恢复原始大小。
{
"in": {
"durationMs": 550,
"easing": "outBack",
"stagger": { "unit": "char", "delayMs": 45, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 0, "dy": -1.2, "scaleX": 0.75, "scaleY": 1.35 },
{ "t": 0.7, "alpha": 1, "dy": 0, "scaleX": 1.3, "scaleY": 0.7 },
{ "t": 1, "alpha": 1, "dy": 0, "scaleX": 1, "scaleY": 1 }
]
},
"out": {
"durationMs": 350,
"easing": "inQuad",
"stagger": { "unit": "none", "delayMs": 0, "from": "start" },
"keyframes": [
{ "t": 0, "alpha": 1 },
{ "t": 1, "alpha": 0, "scaleY": 0.6 }
]
}
}
6. 供 AI 使用的提示词模板
请复制以下框内内容交给 AI。推荐的用法是:先贴上本规范文档全文,紧接着再贴这份模板(如果不附带规范文档、只把模板单独交给 AI,AI 容易在取值范围或 easing 名称上出错)。
以下为复制起点
你是 movlyric 的歌词动画设计师。请严格遵循上面贴出的“AnimationSpec v2 规范文档”,创建一个 AnimationSpec 的 JSON。
输出规则:
- 只输出 JSON(不要写说明、前言或结语;可以用代码围栏包裹 JSON)
- 根节点为 {"in": ..., "out": ...},仅在需要时加入 "activePulse"
- keyframes 为 2~8 个,t 须在 0..1 范围内严格递增,首个必须 t=0,末个必须 t=1
- keyframe 中只能写 t, alpha(0..1), scale(0.1..3), scaleX(0.1..3), scaleY(0.1..3), dx(-2..2), dy(-2..2), rotate(-360..360), tint(0..16777215), strokeWidth(0..20), strokeColor(0..16777215), glow(0..40), glowColor(0..16777215)。单位: dx/dy 是相对字号的比例(正值=右/下),rotate 是度,strokeWidth/glow 是 px,颜色为 0xRRGGBB 形式的整数
- **不想改变的属性请不要写。** 写了 tint 会让该文字停止随演唱变色,固定为指定颜色。不写 strokeWidth/strokeColor 的话样式本身的描边会保留(不写 ≠ 0)
- 想要发光效果时使用 glow(无偏移的光晕)。省略 glowColor 会以文字自身颜色发光
- 挤压拉伸(squash & stretch)动作通过让 scaleX 与 scaleY 反方向变化来实现
- 请遵守 durationMs(整数 50..4000) / stagger.delayMs(整数 0..500) / activePulse.periodMs(整数 200..4000)。stagger 的 unit("none"/"char"/"word")、delayMs、from("start"/"end"/"center") 三项全部必填
- easing 只能使用规范文档缓动一览中的 31 种名称之一(须严格一致)
- out 阶段 t=0 的 keyframe 必须是恒等值(相当于 alpha 1, scale 1, dx 0, dy 0, rotate 0)
- 不要写不存在的属性、注释、末尾逗号、表达式
想要的动作:
{在此写下你想要的动作}
(仅在想修改已有动画时,请在下方粘贴当前的 JSON。若是新建,请删除这一整节)
当前 JSON:
{当前 JSON}
复制到此为止