# 动画、关键帧、转场

这份文档描述的是当前时间线和动画系统的真实行为。

## 1. 动画调度模型

每个顶层 clip 的生命周期：
1. 等待到 `start`
2. 把节点加入场景
3. 在该 clip 的有效时长内执行动画、关键帧和转场
4. clip 生命周期结束

并行关系：
- `animations` 和 `keyframes` 可以并行
- 非 `audio` clip 的 `transition` 也会并行执行
- `audio` clip 会跑音量自动化，而不是视觉转场

## 2. `Animation`

```ts
interface Animation {
  type: AnimationType;
  duration: number;
  delay?: number;
  easing?: EasingFunction;
  loop?: boolean | number;
  from?: any;
  to?: any;
  filter?: FilterType;
  direction?: "left" | "right" | "up" | "down";
  distance?: number;
}
```

当前支持的类型：
- `fadeIn`
- `fadeOut`
- `move`
- `scale`
- `rotate`
- `filter`
- `slideIn`
- `slideOut`
- `zoomIn`
- `zoomOut`

## 3. 每个字段的行为

### `duration`
- 单位：秒
- 必填
- 如果 `loop` 开启，单次循环仍以这个值为基准

### `delay`
- 单位：秒
- 可选
- 同一个 `delay` 的动画会并行执行
- 不同 `delay` 的动画按时间点依次执行

### `easing`
- 可选
- 未填时默认 `linear`

### `loop`
- 类型：`boolean | number`
- `true`：在当前 clip 剩余时长内尽可能重复执行
- 数字：表示执行次数
- 如果总循环时长超过 clip 时长，会自动截断

### `from`

当前真正使用到 `from` 的类型：
- `scale`
- `rotate`
- `move`
- `filter`

说明：
- `move.from` 应该是 `{ x, y }`
- `scale.from` 可以是数字或二维值
- `rotate.from` 应该是数字
- `filter.from` 应该是数字

### `to`

当前真正使用到 `to` 的类型：
- `scale`
- `rotate`
- `move`
- `filter`

说明：
- `move.to` 应该是 `{ x, y }`
- `filter.to` 应该是数字

### `direction`

当前用于：
- `slideIn`
- `slideOut`

部分模板也会给某些预置动画塞 `direction`

### `distance`

当前用于：
- `slideIn`
- `slideOut`

默认值：
- 未填时通常用 `200`

## 4. 各动画类型详解

### `fadeIn`
- 起始透明度设为 `0`
- 动画到原始透明度

### `fadeOut`
- 从当前透明度过渡到 `0`

### `zoomIn`
- 起始缩放设为 `[0, 0]`
- 动画到原始缩放

### `zoomOut`
- 从当前缩放过渡到 `[0, 0]`

### `slideIn`
- 起始位置根据 `direction + distance` 偏移
- 同时从透明度 `0` 过渡回来

### `slideOut`
- 结束时根据 `direction + distance` 偏移出去
- 同时透明度过渡到 `0`

### `scale`
- 如果设置了 `from`，会先把缩放设到 `from`
- 再过渡到 `to`
- 没有 `to` 时回到原始缩放

### `rotate`
- 如果设置了 `from`，会先设到 `from`
- 再过渡到 `to`
- 没有 `to` 时默认转到 `360`

### `filter`
- `filter` 字段指定滤镜名
- 支持 `blur`、`brightness`、`contrast`、`saturate`、`hue`、`grayscale`、`sepia`、`invert`
- 通过 Revideo 的 `node.filters.*` signal 执行动画

```json
{
  "type": "filter",
  "filter": "blur",
  "duration": 0.8,
  "from": 0,
  "to": 24,
  "easing": "easeOutCubic"
}
```

### `move`
- `from` 和 `to` 都应该写成对象
- 例子：

```json
{
  "type": "move",
  "duration": 1,
  "from": { "x": -200, "y": 0 },
  "to": { "x": 0, "y": 0 }
}
```

## 5. `keyframes`

```ts
interface Keyframe {
  property: string;
  frames: KeyframeFrame[];
}

interface KeyframeFrame {
  time: number;
  value: any;
  easing?: EasingFunction;
}
```

当前确认支持的属性：
- `x`
- `y`
- `position`
- `opacity`
- `scale`
- `rotation`
- `width`
- `height`
- `fill`
- `stroke`
- `filters.blur`
- `filters.brightness`
- `filters.contrast`
- `filters.saturate`
- `filters.hue`
- `filters.grayscale`
- `filters.sepia`
- `filters.invert`
- `start`
- `end`
- `drawStart`
- `drawEnd`

行为说明：
- `frames` 会先按 `time` 排序
- 第一个关键帧会直接设值
- 后续关键帧之间按插值执行
- 最后一个关键帧之后会等待到 clip 结束

## 6. `Transition`

```ts
interface Transition {
  type:
    | "fade"
    | "slide"
    | "zoom"
    | "wipe"
    | "blur"
    | "push"
    | "iris"
    | "spin"
    | "lightSweep";
  duration: number;
  direction?: "left" | "right" | "up" | "down";
  easing?: EasingFunction;
  blur?: number;
  intensity?: number;
  rotation?: number;
  scale?: number;
}
```

当前支持：
- `fade`
- `slide`
- `zoom`
- `wipe`
- `blur`
- `push`
- `iris`
- `spin`
- `lightSweep`

真实行为：
- 每个转场包含进场和出场两段
- 两段都使用 `transition.duration`
- 如果两段总时长超过 clip 时长，会自动截断到一半

## 7. 各转场类型详解

### `fade`
- 进场：透明度 `0 -> 原始透明度`
- 出场：原始透明度 `-> 0`

### `slide`
- 进场：从画布外按方向滑入
- 出场：按方向滑出到画布外
- 同时带透明度变化

### `zoom`
- 进场：从较小缩放进入并淡入
- 出场：放大并淡出

### `wipe`
- 通过缩放轴近似实现擦除效果
- 左右方向收缩水平轴
- 上下方向收缩垂直轴

### `blur`
- 进出场时组合透明度与模糊变化

### `push`
- 按方向推动 clip 进出场

### `iris`
- 使用类似径向开合的方式揭示画面

### `spin`
- 在转场中叠加旋转

### `lightSweep`
- 在转场中叠加亮度扫光

## 8. Easing 列表

当前完整支持：
- `linear`
- `easeInSine`
- `easeOutSine`
- `easeInOutSine`
- `easeInQuad`
- `easeOutQuad`
- `easeInOutQuad`
- `easeInCubic`
- `easeOutCubic`
- `easeInOutCubic`
- `easeInQuart`
- `easeOutQuart`
- `easeInOutQuart`
- `easeInBack`
- `easeOutBack`
- `easeInOutBack`
- `easeInElastic`
- `easeOutElastic`
- `easeInOutElastic`
- `easeInBounce`
- `easeOutBounce`
- `easeInOutBounce`

## 9. 音频时间控制

`audio` clip 除了普通 `start` / `duration` 外，还支持：
- `source.start`
- `source.end`
- `fadeIn`
- `fadeOut`
- `pan`
- `detune`
- `gainEnvelope`

这几项共同决定：
- 何时开始播放
- 播放素材的哪个片段
- 播放过程中音量如何变化
- 播放速度、立体声位置和更细粒度的增益自动化

## 10. 示例：标题进场 + 停留 + 出场

```json
{
  "type": "text",
  "start": 0,
  "duration": 4,
  "text": "Hello",
  "transform": {
    "x": "50%",
    "y": "50%"
  },
  "style": {
    "fontSize": 72,
    "fill": "#ffffff"
  },
  "animations": [
    {
      "type": "slideIn",
      "direction": "up",
      "distance": 120,
      "duration": 0.6,
      "easing": "easeOutCubic"
    },
    {
      "type": "fadeOut",
      "delay": 3.2,
      "duration": 0.6,
      "easing": "easeInSine"
    }
  ],
  "transition": {
    "type": "fade",
    "duration": 0.25
  }
}
```