Skip to content

Timeline Module Architecture

Timeline 系统的实现说明和组件关系。

概览

Timeline 模块采用分层结构,每个部分处理单一的职责。

组件布局

┌─────────────────────────────────────────────────────────┐
│         TimelineController (外观/Facade)                 │
│         - 实现 ITimelineTarget                           │
│         - 编排所有功能                                    │
├─────────────────────────────────────────────────────────┤
│  ┌──────────────────────┐  ┌──────────────────────┐  │
│  │   AnimationState     │  │   TimeCalculator      │  │
│  │   (状态机)            │  │   (时间数学运算)       │  │
│  │                      │  │                       │  │
│  │   - play/pause/      │  │   - 缩放              │  │
│  │     resume/stop      │  │   - 偏移              │  │
│  │   - 速度控制          │  │   - Delta 计算        │  │
│  │   - 事件              │  │   - 统计              │  │
│  └──────────────────────┘  └──────────────────────┘  │
│           │                        │                    │
│           │                        │                    │
│           └──────────┬─────────────┘                    │
│                      ▼                                  │
│  ┌──────────────────────────────────────────────┐     │
│  │         TimeUpdateModeHelper                  │     │
│  │         (策略模式)                              │     │
│  │                                                │     │
│  │         - Fixed Delta (固定增量)               │     │
│  │         - Variable Delta (可变增量)            │     │
│  └──────────────────────────────────────────────┘     │
├─────────────────────────────────────────────────────────┤
│  ┌──────────────────────────────────────────────┐      │
│  │         Event System (观察者)                  │      │
│  │                                                │      │
│  │         - 状态变化事件                         │      │
│  │         - 时间变化事件                         │      │
│  │         - 速度变化事件                         │      │
│  └──────────────────────────────────────────────┘      │
└─────────────────────────────────────────────────────────┘

职责

TimelineController

职责: - 提供公共 API(实现 ITimelineTarget) - 协调 AnimationStateTimeCalculator 和事件 - 拥有监听器的注册和分发 - 为旧代码提供兼容性方法 - 跟踪帧计数和统计信息 - 检测回退预览模式

主要特性: - 实现 ITimelineTarget 接口以保持 API 一致性 - 组合模式:委托给 AnimationStateTimeCalculator - 外观(Facade)模式:隐藏内部复杂性 - 观察者模式:用于状态/时间变化的事件系统 - 兼容层:提供 startAnimation, pauseAnimation 方法

方法: - 播放: start(), pause(), resume(), stop() - 时间: setTime(), setSpeed(), setTimeScale(), setTimeOffset(), setTimeUpdateMode() - 更新: update(rafNow?) – 返回调整后的时间 - 查询: getCurrentTime(), getFrameTime(), isFallbackPreviewMode(), getStats() - 状态: isPlaying(), isPaused(), isStopped(), getPlaybackState() - 事件: addEventListener(), removeEventListener(), clearEventListeners()

AnimationState

职责: - 存储播放状态(播放中/暂停/停止) - 管理动画速度 - 触发状态变化事件 - 实现带有验证的有限状态机

状态机:

    STOPPED
       │ play()
    PLAYING ◄─────────┐
       │              │
       │ pause()      │ resume()
       ▼              │
    PAUSED            │
       │              │
       └── stop() ────┘

主要特性: - 状态验证(例如,未播放时无法暂停) - 速度限制(最小 0.1x) - 状态变化时触发事件 - 状态信息查询

方法: - 控制: play(speed?), pause(), resume(), stop(), reset() - 速度: setSpeed(speed), getSpeed() - 查询: getState(), isPlaying, isPaused, isStopped, getStateInfo() - 事件: addEventListener(), removeEventListener(), clearEventListeners()

TimeCalculator

职责: - 处理时间缩放、偏移和 Delta 更新 - 通过 TimeUpdateModeHelper 应用不同的更新策略 - 跟踪帧时间和上次更新时间 - 计算调整后的时间(带缩放和偏移)

时间计算流程:

原始输入 (rafNow)
calculateDeltaTime() [通过 TimeUpdateModeHelper]
应用 timeScale * animationSpeed
累积 frameTime
应用 timeOffset 和 timeScale
adjustedTime (输出)

主要特性: - 帧时间跟踪 (frameTime 属性) - 上次更新时间跟踪(用于 variable delta 模式) - 可配置的时间缩放和偏移 - 支持 fixed 和 variable delta 模式 - 统计和克隆支持

方法: - 时间控制: setTime(time), resetTime(), setTimeScale(scale), setTimeOffset(offset) - 更新模式: setTimeUpdateMode(mode), getTimeUpdateMode() - 速度: setAnimationSpeed(speed), getAnimationSpeed() - 计算: calculateTime(rafNow, isPlaying, isPaused) – 返回 TimeCalculationResult - 查询: getAdjustedTime(), getTimeScale(), getTimeOffset(), getStats() - 实用工具: clone(), updateConfig()

TimeUpdateMode

职责: - 封装更新策略 (fixed_delta, variable_delta) - 提供 Delta 计算的静态辅助方法 - 验证配置并暴露描述信息

TimeUpdateMode Enum:

enum TimeUpdateMode {
  FIXED_DELTA = 'fixed_delta',      // 确定性的固定步长
  VARIABLE_DELTA = 'variable_delta' // 依赖帧率的步长
}

TimeUpdateModeHelper 方法: - calculateDeltaTime(params) – 根据模式计算 Delta - isValidMode(mode) – 用于模式验证的类型守卫 - fromString(modeString) – 将字符串转换为枚举 - getDefaultFixedDeltaTime() – 返回默认固定 Delta (~60 FPS) - getDefaultMaxDeltaTime() – 返回默认最大 Delta (50ms) - getModeDescription(mode) – 返回人类可读的描述

更新策略:

Fixed Delta: - 直接使用 fixedDeltaTime 值 - 确定性且不依赖帧率 - 适用于:物理模拟、确定性动画

Variable Delta: - 根据实际帧时间差计算 - 限制在 maxDeltaTime 以防止极值 - 适用于:平滑播放、依赖帧率的动画

数据流

时间更新流

  1. 客户端调用 timeline.update(rafNow) (可选 rafNow,默认为 performance.now())
  2. Controller 委托TimeCalculator.calculateTime(rafNow, isPlaying, isPaused)
  3. Calculator:
    • 检查是否正在播放且未暂停
    • 根据模式调用 TimeUpdateModeHelper.calculateDeltaTime()
    • timeScale * animationSpeed 应用于 Delta
    • 累积 frameTime
    • 计算 adjustedTime(带偏移和缩放)
  4. Controller:
    • 如果 shouldUpdate 为真,增加 frameCount
    • 返回 adjustedTime
  5. 事件系统: (如果时间发生显著变化)
    • 触发 timeChange 事件
    • 通知所有监听器

状态变化流

  1. 客户端调用 start(), pause(), resume(), 或 stop()
  2. Controller 转发AnimationState 方法
  3. AnimationState:
    • 验证状态转换(例如,未播放时无法暂停)
    • 更新内部状态
    • 触发状态变化事件 (play, pause, resume, stop)
  4. Controller:
    • 将事件中继给它自己的监听器
    • 事件系统通知所有注册的监听器

速度变化流

  1. 客户端调用 setSpeed(speed)
  2. Controller:
    • 更新 AnimationState.setSpeed(speed)
    • 更新 TimeCalculator.setAnimationSpeed(speed)
  3. AnimationState:
    • 限制速度(最小 0.1x)
    • 如果速度实际发生变化,触发 speedChange 事件
  4. 事件系统: 通知监听器速度变化

设计模式

组合模式 (Composition Pattern)

Controller 组合了多个助手:

class TimelineController {
  private animationState = new AnimationState();
  private timeCalculator = new TimeCalculator();

  start(speed?: number) {
    this.animationState.play(speed);
  }

  update(rafNow?: number): number {
    return this.timeCalculator.calculateTime(
      rafNow,
      this.animationState.isPlaying,
      this.animationState.isPaused
    ).adjustedTime;
  }
}

外观模式 (Façade Pattern)

TimelineController 通过实现 ITimelineTarget 接口,将协调的复杂性隐藏在一个简单的 API 后面。

观察者模式 (Observer Pattern)

事件监听器订阅时间/状态变化事件:

timeline.addEventListener((event) => {
  // 对 timeline 事件做出反应
});

状态机模式 (State Machine Pattern)

AnimationState 实现了带有经过验证的转换的有限状态机: - 状态: STOPPED, PLAYING, PAUSED - 转换经过验证(例如,未播放时无法暂停)

策略模式 (Strategy Pattern)

TimeUpdateModeHelper 封装了不同的 Delta 计算策略: - FIXED_DELTA – 确定性的固定步长 - VARIABLE_DELTA – 依赖帧率的步长

适配器模式 (Adapter Pattern)

兼容性方法将新 API 适配给旧代码:

startAnimation(speed)  start(speed)
pauseAnimation()  pause()
setAnimationTime(time)  setTime(time)

可扩展性

插件模型

class CustomTimelineExtension {
  constructor(timeline: TimelineController) {
    timeline.addEventListener(event => this.handle(event));
  }

  private handle(event: AnimationStateChangeEvent) {
    switch (event.type) {
      case 'play':
        this.onPlay();
        break;
      case 'timeChange':
        this.onTimeChange(event.data.time);
        break;
    }
  }
}

配置驱动行为

const config: TimelineConfig = {
  timeScale: 1.0,              // 时间缩放因子
  timeOffset: 0.0,             // 时间偏移(秒)
  timeUpdateMode: 'fixed_delta', // 或 'variable_delta'
  animationSpeed: 1.0,         // 动画速度倍率
  fixedDeltaTime: 0.016,       // 固定 Delta 时间 (~60 FPS)
  maxDeltaTime: 0.05,          // 最大 Delta 时间 (50ms)
};

const timeline = new TimelineController(config);

接口抽象

TimelineController 实现 ITimelineTarget 以保持 API 一致性:

interface ITimelineTarget {
  // 播放控制
  startAnimation(speed?: number): void;
  pauseAnimation(): void;
  resumeAnimation(): void;
  stopAnimation(): void;

  // 时间控制
  setAnimationTime(time: number): void;
  setAnimationSpeed(speed: number): void;
  getAnimationSpeed(): number;

  // 时间缩放
  setTimeScale(scale: number): void;
  getTimeScale(): number;
  setTimeOffset(offset: number): void;
  getTimeOffset(): number;

  // 更新模式
  setTimeUpdateMode(mode: 'fixed_delta' | 'variable_delta'): void;
  getTimeUpdateMode(): 'fixed_delta' | 'variable_delta';

  // 查询
  getCurrentTime(): number;
  supportsAnimation(): boolean;
}

回退预览模式

frameTime < -0.5 时,Timeline 会检测到回退预览场景:

if (timeline.isFallbackPreviewMode()) {
  // 处理预览模式
}

这对于可能将时间设置为负值的导出/录制场景非常有用。

性能考量

  • 事件数组和批量处理使开销最小化。
  • 缓存可防止冗余计算。
  • 及时清理监听器(避免泄漏),并优先使用 WeakMap 存储临时数据。

测试策略

单元测试

describe('AnimationState', () => {
  it('transitions STOPPED → PLAYING', () => {
    const state = new AnimationState();
    state.play();
    expect(state.getState()).toBe(AnimationPlaybackState.PLAYING);
  });
});

集成测试

describe('TimelineController', () => {
  it('coordinates updates correctly', () => {
    const timeline = new TimelineController();
    timeline.start();
    timeline.update(0.016);
    expect(timeline.getPlaybackState()).toBe(AnimationPlaybackState.PLAYING);
  });
});

性能测试

describe('TimeCalculator performance', () => {
  it('hits 10k updates < 10ms', () => {
    const calc = new TimeCalculator(config);
    const t0 = performance.now();
    for (let i = 0; i < 10_000; i++) calc.calculateTime(0.016);
    expect(performance.now() - t0).toBeLessThan(10);
  });
});

总结

Timeline 架构包含: 1. 每个组件单一职责。 2. 开/闭可扩展性。 3. 通过接口进行依赖倒置。 4. 接口隔离(小型、专注的 API)。 5. 以组合为中心的设计。

结果是一个易于测试、可扩展且可维护的 Timeline 子系统。