这是一个时间轴发射器类,用于管理基于时间的事件触发。它继承自
EventEmitter
,能够根据时间进度自动触发相应的事件。
详细解读
1. 导入和类型定义
import EventEmitter from "eventemitter3"
import { parseTimeToSeconds as _ps } from "./tool"
// 定义时间轴发射器支持的事件类型
type TimelineEmitterEvents<T> = {
exe: (payload: T) => void // 执行事件,携带载荷数据
end: () => void // 结束事件
}
2. TimelineEmitter 类结构
export class TimelineEmitter<T extends { time: number | string }>
extends EventEmitter<TimelineEmitterEvents<T>> {
$input!: T[]; // 输入的事件数组
$cur_index: number = 0; // 当前处理的事件索引
泛型约束: T extends { time: number | string }
确保所有事件对象都必须包含 time
属性。
3. 构造函数
constructor(input: T[]) {
super();
this.watch(input) // 初始化时设置输入数据
}
4. 核心方法解析
watch() - 监听输入数据
watch(input: T[]) {
this.$input = input
this.$cur_index = 0
this.$input?.sort((a, b) => _ps(a.time) - _ps(b.time)) // 按时间排序
}
作用:
- 设置输入的事件数组
- 重置当前索引为 0
- 按时间顺序对事件进行排序
now 属性设置器 - 核心逻辑
set now(time: number) {
// 1. 检查是否已处理完所有事件
if (this.$cur_index >= this.$input.length) {
this.emit('end') // 触发结束事件
return
}
// 2. 遍历从当前索引开始的所有事件
for (let index = this.$cur_index; index < this.$input.length; index++) {
const element = this.$input[index];
// 3. 检查事件时间是否到达
if (_ps(element.time) <= time) {
this.$cur_index = index + 1 // 更新索引
this.emit('exe', element); // 触发执行事件
}
}
}
工作原理
1. 时间轴事件触发机制
// 示例:事件数组
const events = [
{ time: 0, type: "SHOW_FOCUS_POINT", payload: { pointId: 1 } },
{ time: 5, type: "SHOW_BUBBLE", payload: { id: "bubble1" } },
{ time: 10, type: "SHOW_FRAME_VIDEO", payload: { video: "scene1.mp4" } },
{ time: 15, type: "SHOW_CAMERA_POS", payload: { bg: "camera-bg.png" } },
]
// 创建时间轴发射器
const timelineEmitter = new TimelineEmitter(events)
// 监听执行事件
timelineEmitter.on("exe", (event) => {
console.log("触发事件:", event.type, "时间:", event.time)
})
// 监听结束事件
timelineEmitter.on("end", () => {
console.log("所有事件执行完毕")
})
// 模拟时间推进
timelineEmitter.now = 0 // 触发第一个事件
timelineEmitter.now = 5 // 触发第二个事件
timelineEmitter.now = 10 // 触发第三个事件
timelineEmitter.now = 15 // 触发第四个事件
timelineEmitter.now = 20 // 触发结束事件
2. 在 write.ts 中的使用
// 创建时间轴发射器
timelineEmitterMain = new TimelineEmitter(events)
// 监听事件执行
timelineEmitterMain.on("exe", (event: WrtTimelineEvent) => {
__handleTimelineEvent(event) // 处理时间轴事件
})
// 更新时间轴(由视频播放进度驱动)
function __updateTimelineEmitters(time: number) {
if (timelineEmitterMain) {
timelineEmitterMain.now = time // 推进时间轴
}
}
设计模式分析
1. 观察者模式 (Observer Pattern)
// 事件监听
timelineEmitter.on("exe", (event) => {
// 处理事件
})
// 事件触发
this.emit("exe", element)
2. 状态机模式 (State Machine)
// 状态转换:根据时间推进状态
set now(time: number) {
// 根据当前时间决定触发哪些事件
// 状态:未开始 -> 执行中 -> 已结束
}
3. 迭代器模式 (Iterator Pattern)
// 按顺序遍历事件
for (let index = this.$cur_index; index < this.$input.length; index++) {
const element = this.$input[index]
// 处理当前事件
}
使用场景
1. 视频播放进度同步
// 视频播放时同步触发时间轴事件
videoTeacherProxy.on("tick", (time) => {
timelineEmitterMain.now = time // 更新时间轴
})
2. 教学场景事件控制
// 根据播放进度显示不同的教学元素
const teachingEvents = [
{ time: 0, type: "SHOW_TITLE", payload: { text: "写作技巧" } },
{ time: 5, type: "SHOW_FOCUS", payload: { pointId: 1 } },
{ time: 10, type: "SHOW_EXAMPLE", payload: { content: "示例文本" } },
]
3. 动画序列控制
// 控制动画播放序列
const animationEvents = [
{ time: 0, type: "FADE_IN", payload: { element: "title" } },
{ time: 1000, type: "SLIDE_IN", payload: { element: "content" } },
{ time: 2000, type: "HIGHLIGHT", payload: { element: "focus" } },
]
优点总结
- ✅ 时间精确控制: 基于时间的事件触发
- ✅ 事件驱动: 松耦合的事件处理机制
- ✅ 自动排序: 事件按时间自动排序
- ✅ 状态管理: 清晰的状态转换逻辑
- ✅ 类型安全: TypeScript 提供类型检查
- ✅ 易于扩展: 支持自定义事件类型
核心特性
1. 时间驱动
- 根据时间进度自动触发事件
- 支持毫秒级精确控制
2. 事件队列
- 按时间顺序处理事件
- 防止事件重复触发
3. 状态同步
- 与视频播放进度同步
- 支持暂停、恢复、跳转
4. 类型安全
- 泛型支持自定义事件类型
- 编译时类型检查
这个 TimelineEmitter
类是一个设计良好的时间轴事件管理器,为视频播放和教学场景提供了强大的事件控制能力。它通过事件驱动的方式,实现了时间轴与业务逻辑的解耦,是一个很好的时间轴事件处理解决方案。