@minto-ai/type-writer
Version:
流式打字机效果组件,提供逐字符显示文本的打字机效果
149 lines (128 loc) • 3.74 kB
text/typescript
import type { PublicCustomEventName, TypeWriterOptions } from './types'
import { TypeWriterStatus } from './types'
import { createEventBus } from './utils/event-bus'
const defaultOptions: Readonly<Required<TypeWriterOptions>> = {
speed: 100,
}
class TypeWriter {
private options: TypeWriterOptions
private status: TypeWriterStatus
private wordQueue: string[]
private timer: NodeJS.Timeout | null
private outputText: string
private isSendComplete: boolean
private generator: Generator<string | undefined, void, unknown> | null
private $bus = createEventBus<PublicCustomEventName>()
constructor(options: TypeWriterOptions) {
this.options = {
...defaultOptions,
...options,
}
this.status = TypeWriterStatus.PENDING
this.wordQueue = []
this.outputText = ''
this.timer = null
this.isSendComplete = false
this.generator = this.createTextGenerator()
}
private* createTextGenerator(): Generator<string | undefined, void, unknown> {
while (true) {
if (this.isSendComplete) {
return
}
if (this.wordQueue.length === 0) {
yield
continue
}
const word = this.wordQueue.shift()!
yield word
}
}
/**
* 发送文本
* @param text 要发送的文本
* @returns 打字机实例
*/
public send(text: string): TypeWriter {
if (this.isSendComplete || this.status === TypeWriterStatus.COMPLETED) {
return this
}
if ([undefined, null, ''].includes(text)) {
return this
}
this.wordQueue.push(...text.split(''))
if (this.status === TypeWriterStatus.PENDING) {
this.status = TypeWriterStatus.EXECUTING
this.execute()
}
return this
}
/**
* 手动结束打字机
*/
public end(): void {
this.isSendComplete = true
}
/**
* 手动销毁打字机
*/
public finish(): void {
if (this.timer) {
clearTimeout(this.timer)
this.timer = null
}
this.wordQueue = []
this.generator = null
this.status = TypeWriterStatus.COMPLETED
this.emit('complete')
this.$bus.clear()
}
private execute(): void {
const result = this.generator!.next()
if (result.done) {
this.generator = null
this.status = TypeWriterStatus.COMPLETED
this.emit('complete')
this.$bus.clear()
return
}
if (result.value) {
this.outputText += result.value
this.emit('change', this.outputText)
}
this.timer = setTimeout(() => {
this.execute()
}, this.options.speed)
}
/**
* 获取当前输出的文本
* @returns 当前输出的文本
*/
public getText(): string {
return this.outputText
}
private emit<T>(eventName: PublicCustomEventName, data?: T): void {
this.$bus.emit(eventName, data)
}
/**
* 监听事件
* @param eventName 事件名称,支持以下事件:
* - 'change': 文本变化事件,当有新字符输出时触发,回调参数为当前完整文本
* - 'complete': 完成事件,当打字任务完成时触发,无回调参数
* @param callback 回调函数
* @returns 打字机实例
*/
public on(eventName: PublicCustomEventName, callback: (data?: any) => void): TypeWriter {
this.$bus.on(eventName, callback)
return this
}
}
/**
* 创建一个打字机实例
* @param options 配置项
* @param options.speed 打字速度,单位为毫秒,默认值为 100
* @returns 打字机实例
*/
export function createTypeWriter(options: TypeWriterOptions): TypeWriter {
return new TypeWriter(options)
}