123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- 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();
- }
- }
- }
|