时间轴事件管理器


这是一个时间轴发射器类,用于管理基于时间的事件触发。它继承自 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 类是一个设计良好的时间轴事件管理器,为视频播放和教学场景提供了强大的事件控制能力。它通过事件驱动的方式,实现了时间轴与业务逻辑的解耦,是一个很好的时间轴事件处理解决方案。


文章作者: 高红翔
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 高红翔 !
  目录