AnimationSpec v2 规范文档 — 自定义"文字动作"

本文档译自日语原版。日语版为正式版本,如有出入以日语版为准。

本文档是 movlyric 文字动作(歌词动画)定义“AnimationSpec v2”的完整规范。 在应用的“样式编辑器 →“动作”标签 → 文字动作 →“自定义”→ 用 AI 生成…”中使用(在背景绘制图形的“背景特效”是另一套体系, 其规范请见 GRAPHIC_SPEC(背景特效))。 以 v1(仅含动效)编写的 JSON 在 v2 中依然有效。v2 新增了颜色、描边、发光以及分轴缩放。 本文档的目标是:只读过此文档的 AI(或人类)也能写出可以直接粘贴到应用中并被接受的 JSON。 文档末尾附有可交给 AI 使用的提示词模板。将本文档整体作为提示词的一部分贴入同样可以正常使用。

1. 概述

movlyric 是一个按时间在画面上显示歌词(乐句)的视频引擎。 一句歌词按以下三个阶段运动。

  1. 入场(in):从乐句显示窗口的开头(乐句开始发声前约 1 秒)起,文字/单词出现的动作
  2. 显示中(稳定态):入场结束后的静止状态。可选叠加 activePulse(正弦波脉动)
  3. 退场(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)。当它比显示窗口更长时,整个时间轴会被等比压缩(这样即使窗口较短,文字也不会一直不出现)。歌词字数多、char stagger 使用较大 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}

复制到此为止