import { Label } from 'cc'; import { Logger } from './Logger'; /* ----------------------------------------------- 基础用法 const typewriter = new VerbatimEffect(this.label, { speed: 0.1, // 每个字符显示间隔 delay: 1.0, // 开始前的延迟 callbackDelay: 0.5 // 完成回调延迟 }); //添加文字 typewriter.addText("第一段文字") .addText("第二段文字") .addText(["第三段文字", "第四段文字"]); //设置回调 typewriter.onComplete(() => { Logger.log("所有文字显示完成"); }); //开始播放 typewriter.start(); ----------------------------------------------- 显示数组用法 const verseArr = this.seleteData.verse.split(","); const typewriter = new VerbatimEffect(this.verseLabel1, { speed: 0.15, preserveSpaces: true // 保留空格 }); typewriter.addText(verseArr) .onTextComplete((text, index) => { Logger.log(`第${index + 1}句显示完成: ${text}`); }) .onComplete(() => { Logger.log("所有诗句显示完成"); }) .start(); ----------------------------------------------- 高阶用法 const typewriter = new VerbatimEffect(this.label); typewriter.addText("Hello World!") .addText("This is Typewriter Effect.") .onChar((char) => { // 每个字符显示时的音效 AudioManager.playTypingSound(); }) .onTextStart((text, index) => { Logger.log(`开始显示第${index + 1}段文字: ${text}`); }) .onTextComplete((text, index) => { Logger.log(`第${index + 1}段文字显示完成: ${text}`); }) .onComplete(() => { Logger.log("全部文字显示完成"); }) .start(); */ /** * 打字机效果配置选项 */ interface WriterOptions { /** 每个字符的显示间隔时间(秒)默认0.1 */ speed?: number; /** 开始播放前的延迟时间(秒)默认0 */ delay?: number; /** 播放完成后的回调延迟(秒)默认0 */ callbackDelay?: number; /** 是否自动开始播放 默认true */ autoStart?: boolean; /** 是否保留空格 默认false */ preserveSpaces?: boolean; } /** * 打字机效果类 - 实现文字逐字显示效果 */ export class VerbatimEffect { private label: Label; private textQueue: string[] = []; private currentIndex: number = 0; private isPlaying: boolean = false; private options: WriterOptions; private onCompleteCallback: Function | null = null; private onCharCallback: ((char: string) => void) | null = null; private onTextStartCallback: ((text: string, index: number) => void) | null = null; private onTextCompleteCallback: ((text: string, index: number) => void) | null = null; /** * 构造函数 * @param label 要显示文字的Label组件 * @param options 配置选项 */ constructor(label: Label, options: WriterOptions = {}) { this.label = label; this.options = { speed: 0.1, delay: 0, callbackDelay: 0, autoStart: true, preserveSpaces: false, ...options }; } /** * 添加要显示的文字 * @param text 文字内容(可以是字符串或字符串数组) * @return 当前实例(支持链式调用) */ public addText(text: string | string[]): VerbatimEffect { if (Array.isArray(text)) { this.textQueue.push(...text); } else { this.textQueue.push(text); } return this; } /** * 设置完成回调 * @param callback 所有文字显示完成后的回调 * @return 当前实例(支持链式调用) */ public onComplete(callback: Function): VerbatimEffect { this.onCompleteCallback = callback; return this; } /** * 设置字符显示回调 * @param callback 每个字符显示时的回调 * @return 当前实例(支持链式调用) */ public onChar(callback: (char: string) => void): VerbatimEffect { this.onCharCallback = callback; return this; } /** * 设置单个文本开始显示回调 * @param callback 单个文本开始显示时的回调 * @return 当前实例(支持链式调用) */ public onTextStart(callback: (text: string, index: number) => void): VerbatimEffect { this.onTextStartCallback = callback; return this; } /** * 设置单个文本显示完成回调 * @param callback 单个文本显示完成时的回调 * @return 当前实例(支持链式调用) */ public onTextComplete(callback: (text: string, index: number) => void): VerbatimEffect { this.onTextCompleteCallback = callback; return this; } /** * 开始播放文字 */ public start(): void { if (this.isPlaying || this.textQueue.length === 0) return; this.isPlaying = true; this.currentIndex = 0; this.label.node.active = true; this.label.string = ''; if (this.options.delay && this.options.delay > 0) { this.label.scheduleOnce(() => this.playNextText(), this.options.delay); } else { this.playNextText(); } } /** * 停止播放 */ public stop(): void { this.isPlaying = false; this.label.unscheduleAllCallbacks(); } /** * 跳过当前文本,立即显示完整内容 */ public skipCurrent(): void { if (!this.isPlaying) return; this.label.unscheduleAllCallbacks(); const currentText = this.textQueue[this.currentIndex]; this.label.string = this.options.preserveSpaces ? currentText : currentText.replace(/\s/g, ''); this.handleTextComplete(currentText, this.currentIndex); } /** * 跳过所有,立即显示所有完整内容 */ public skipAll(): void { if (!this.isPlaying) return; this.stop(); let fullText = ''; for (let i = this.currentIndex; i < this.textQueue.length; i++) { fullText += this.options.preserveSpaces ? this.textQueue[i] : this.textQueue[i].replace(/\s/g, ''); } this.label.string = fullText; if (this.onCompleteCallback) { this.executeCallback(this.onCompleteCallback, this.options.callbackDelay); } } /** * 清空文字队列 */ public clear(): void { this.stop(); this.textQueue = []; this.currentIndex = 0; } /** * 播放下一个文本(内部方法) */ private playNextText(): void { if(!this.isPlaying || this.currentIndex >= this.textQueue.length) { this.isPlaying = false; if (this.onCompleteCallback) { this.executeCallback(this.onCompleteCallback, this.options.callbackDelay); } return; } const currentText = this.textQueue[this.currentIndex]; const processedText = this.options.preserveSpaces ? currentText : currentText.replace(/\s/g, ''); const chars = processedText.split(''); let currentPos = 0; let displayText = ''; //触发文本开始回调 if(this.onTextStartCallback) { this.onTextStartCallback(currentText, this.currentIndex); } const updateFunc = () => { if (!this.isPlaying) return; const char = chars[currentPos]; displayText += char; this.label.string = displayText; //触发字符回调 if(this.onCharCallback) { this.onCharCallback(char); } if (++currentPos >= chars.length) { this.label.unschedule(updateFunc); this.handleTextComplete(currentText, this.currentIndex++); } }; this.label.schedule(updateFunc, this.options.speed, chars.length - 1, 0); } /** * 处理文本完成(内部方法) */ private handleTextComplete(text: string, index: number): void { //触发文本完成回调 if (this.onTextCompleteCallback) { this.onTextCompleteCallback(text, index); } //播放下一个文本 this.playNextText(); } /** * 执行回调(带延迟) */ private executeCallback(callback: Function, delay: number = 0): void { if (delay && delay > 0) { this.label.scheduleOnce(() => callback(), delay); } else { callback(); } } }