UNPKG

@minto-ai/type-writer

Version:

流式打字机效果组件,提供逐字符显示文本的打字机效果

149 lines (128 loc) 3.74 kB
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) }