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)
- 协调 AnimationState、TimeCalculator 和事件
- 拥有监听器的注册和分发
- 为旧代码提供兼容性方法
- 跟踪帧计数和统计信息
- 检测回退预览模式
主要特性:
- 实现 ITimelineTarget 接口以保持 API 一致性
- 组合模式:委托给 AnimationState 和 TimeCalculator
- 外观(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
职责: - 存储播放状态(播放中/暂停/停止) - 管理动画速度 - 触发状态变化事件 - 实现带有验证的有限状态机
状态机:
主要特性: - 状态验证(例如,未播放时无法暂停) - 速度限制(最小 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 以防止极值
- 适用于:平滑播放、依赖帧率的动画
数据流
时间更新流
- 客户端调用
timeline.update(rafNow)(可选rafNow,默认为performance.now()) - Controller 委托 给
TimeCalculator.calculateTime(rafNow, isPlaying, isPaused) - Calculator:
- 检查是否正在播放且未暂停
- 根据模式调用
TimeUpdateModeHelper.calculateDeltaTime() - 将
timeScale * animationSpeed应用于 Delta - 累积
frameTime - 计算
adjustedTime(带偏移和缩放)
- Controller:
- 如果
shouldUpdate为真,增加frameCount - 返回
adjustedTime
- 如果
- 事件系统: (如果时间发生显著变化)
- 触发
timeChange事件 - 通知所有监听器
- 触发
状态变化流
- 客户端调用
start(),pause(),resume(), 或stop() - Controller 转发 给
AnimationState方法 - AnimationState:
- 验证状态转换(例如,未播放时无法暂停)
- 更新内部状态
- 触发状态变化事件 (
play,pause,resume,stop)
- Controller:
- 将事件中继给它自己的监听器
- 事件系统通知所有注册的监听器
速度变化流
- 客户端调用
setSpeed(speed) - Controller:
- 更新
AnimationState.setSpeed(speed) - 更新
TimeCalculator.setAnimationSpeed(speed)
- 更新
- AnimationState:
- 限制速度(最小 0.1x)
- 如果速度实际发生变化,触发
speedChange事件
- 事件系统: 通知监听器速度变化
设计模式
组合模式 (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)
事件监听器订阅时间/状态变化事件:
状态机模式 (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 会检测到回退预览场景:
这对于可能将时间设置为负值的导出/录制场景非常有用。
性能考量
- 事件数组和批量处理使开销最小化。
- 缓存可防止冗余计算。
- 及时清理监听器(避免泄漏),并优先使用
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 子系统。