291 lines
7.7 KiB
TypeScript
291 lines
7.7 KiB
TypeScript
import { _decorator, Button, Component, director, Label, Node, tween, Vec3 } from "cc";
|
||
|
||
/**
|
||
* DialogBox配置选项接口
|
||
*/
|
||
export interface IDialogBoxOptions {
|
||
/** 标题,默认为"提示" */
|
||
title?: string;
|
||
/** 内容,默认为空 */
|
||
content?: string;
|
||
/** 确定回调,可选 */
|
||
onConfirm?: () => void;
|
||
/** 取消回调,可选 */
|
||
onCancel?: () => void;
|
||
/** 确定按钮文本,默认为"确定" */
|
||
confirmText?: string;
|
||
/** 取消按钮文本,默认为"取消" */
|
||
cancelText?: string;
|
||
/** 是否显示取消按钮,默认为true */
|
||
showCancel?: boolean;
|
||
/** 场景中DialogBox节点名称,默认为"DialogBox" */
|
||
dialogNodeName?: string;
|
||
hideClose?: boolean;
|
||
}
|
||
|
||
const { ccclass, property } = _decorator;
|
||
|
||
/**
|
||
* 基础对话框组件
|
||
*/
|
||
@ccclass("DialogBox")
|
||
export class DialogBox extends Component {
|
||
// 静态缓存场景中的DialogBox节点
|
||
private static sceneDialogBox: DialogBox | null = null;
|
||
private static dialogNode: Node | null = null;
|
||
|
||
@property(Label)
|
||
titleLabel: Label = null!;
|
||
|
||
@property(Label)
|
||
contentLabel: Label = null!;
|
||
|
||
@property(Button)
|
||
confirmBtn: Button = null!;
|
||
|
||
@property(Label)
|
||
confirmBtnLabel: Label = null!;
|
||
|
||
@property(Button)
|
||
cancelBtn: Button = null!;
|
||
|
||
@property(Label)
|
||
cancelBtnLabel: Label = null!;
|
||
|
||
// 回调函数
|
||
private onConfirmCallback: (() => void) | null = null;
|
||
private onCancelCallback: (() => void) | null = null;
|
||
|
||
start() {
|
||
// 绑定按钮事件
|
||
if (this.confirmBtn) {
|
||
this.confirmBtn.node.on(Button.EventType.CLICK, this.onConfirmClick, this);
|
||
}
|
||
|
||
if (this.cancelBtn) {
|
||
this.cancelBtn.node.on(Button.EventType.CLICK, this.onCancelClick, this);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置对话框内容
|
||
* @param options 配置选项
|
||
*/
|
||
public setContent(options: IDialogBoxOptions = {}): void {
|
||
const {
|
||
title = "提示",
|
||
content = "",
|
||
onConfirm,
|
||
onCancel,
|
||
confirmText = "确定",
|
||
cancelText = "取消",
|
||
showCancel = true,
|
||
} = options;
|
||
|
||
// 设置标题和内容
|
||
if (this.titleLabel) {
|
||
this.titleLabel.string = title;
|
||
}
|
||
if (this.contentLabel) {
|
||
this.contentLabel.string = content;
|
||
}
|
||
|
||
// 设置按钮文本
|
||
if (this.confirmBtnLabel) {
|
||
this.confirmBtnLabel.string = confirmText;
|
||
}
|
||
if (this.cancelBtnLabel) {
|
||
this.cancelBtnLabel.string = cancelText;
|
||
}
|
||
|
||
// 控制取消按钮显示状态
|
||
if (this.cancelBtn) {
|
||
this.cancelBtn.node.active = showCancel;
|
||
}
|
||
|
||
// 保存回调函数
|
||
this.onConfirmCallback = onConfirm || null;
|
||
this.onCancelCallback = onCancel || null;
|
||
}
|
||
|
||
/**
|
||
* 显示对话框(带弹出动画)
|
||
*/
|
||
public show(): void {
|
||
this.node.active = true;
|
||
|
||
// 初始化缩放和透明度
|
||
this.node.setScale(Vec3.ZERO);
|
||
|
||
// 弹出动画:缩放从0到1,带回弹效果
|
||
tween(this.node)
|
||
.to(
|
||
0.3,
|
||
{ scale: Vec3.ONE },
|
||
{
|
||
easing: "backOut", // 回弹缓动效果
|
||
},
|
||
)
|
||
.start();
|
||
}
|
||
|
||
/**
|
||
* 隐藏对话框(带关闭动画)
|
||
*/
|
||
public hide(): void {
|
||
// 停止所有动画
|
||
tween(this.node).stop();
|
||
|
||
// 关闭动画:缩放到0
|
||
tween(this.node)
|
||
.to(
|
||
0.2,
|
||
{ scale: Vec3.ZERO },
|
||
{
|
||
easing: "backIn", // 回缩缓动效果
|
||
},
|
||
)
|
||
.call(() => {
|
||
// 动画结束后隐藏节点
|
||
this.node.active = false;
|
||
// 恢复缩放值,为下次显示做准备
|
||
this.node.setScale(Vec3.ONE);
|
||
})
|
||
.start();
|
||
|
||
// 清理回调
|
||
this.onConfirmCallback = null;
|
||
this.onCancelCallback = null;
|
||
}
|
||
|
||
/**
|
||
* 确定按钮点击事件
|
||
*/
|
||
private onConfirmClick(): void {
|
||
// 执行回调
|
||
if (this.onConfirmCallback) {
|
||
this.onConfirmCallback();
|
||
}
|
||
|
||
// 隐藏对话框
|
||
this.hide();
|
||
}
|
||
|
||
/**
|
||
* 取消按钮点击事件
|
||
*/
|
||
private onCancelClick(): void {
|
||
// 执行回调
|
||
if (this.onCancelCallback) {
|
||
this.onCancelCallback();
|
||
}
|
||
|
||
// 隐藏对话框
|
||
this.hide();
|
||
}
|
||
|
||
onDestroy() {
|
||
// 清理事件监听
|
||
if (this.confirmBtn) {
|
||
this.confirmBtn.node.off(Button.EventType.CLICK, this.onConfirmClick, this);
|
||
}
|
||
|
||
if (this.cancelBtn) {
|
||
this.cancelBtn.node.off(Button.EventType.CLICK, this.onCancelClick, this);
|
||
}
|
||
|
||
// 停止所有动画
|
||
tween(this.node).stop();
|
||
|
||
// 清理静态引用
|
||
if (DialogBox.sceneDialogBox === this) {
|
||
DialogBox.sceneDialogBox = null;
|
||
DialogBox.dialogNode = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 静态方法:显示对话框
|
||
* @param options 配置选项
|
||
*/
|
||
public static showDialog(options: IDialogBoxOptions = {}): void {
|
||
const { dialogNodeName = "DialogBox" } = options;
|
||
|
||
// 获取场景中的DialogBox节点
|
||
if (!DialogBox.sceneDialogBox || !DialogBox.dialogNode?.isValid) {
|
||
DialogBox.findDialogBoxInScene(dialogNodeName);
|
||
}
|
||
|
||
if (!DialogBox.sceneDialogBox) {
|
||
return; // 场景中未找到DialogBox节点
|
||
}
|
||
|
||
// 设置内容并显示
|
||
DialogBox.sceneDialogBox.setContent(options);
|
||
DialogBox.sceneDialogBox.show();
|
||
}
|
||
|
||
/**
|
||
* 静态方法:隐藏当前对话框(带动画)
|
||
*/
|
||
public static hideCurrentDialog(): void {
|
||
if (DialogBox.sceneDialogBox) {
|
||
DialogBox.sceneDialogBox.hide();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 静态方法:立即隐藏当前对话框(无动画)
|
||
*/
|
||
public static hideCurrentDialogImmediately(): void {
|
||
if (DialogBox.sceneDialogBox) {
|
||
// 停止所有动画
|
||
tween(DialogBox.sceneDialogBox.node).stop();
|
||
// 立即隐藏
|
||
DialogBox.sceneDialogBox.node.active = false;
|
||
DialogBox.sceneDialogBox.node.setScale(Vec3.ONE);
|
||
// 清理回调
|
||
DialogBox.sceneDialogBox.onConfirmCallback = null;
|
||
DialogBox.sceneDialogBox.onCancelCallback = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 在场景中查找DialogBox节点
|
||
* @param nodeName 节点名称
|
||
*/
|
||
private static findDialogBoxInScene(nodeName: string): void {
|
||
const scene = director.getScene();
|
||
if (!scene) {
|
||
return;
|
||
}
|
||
|
||
// 递归查找DialogBox节点
|
||
const findNodeRecursively = (node: Node, targetName: string): Node | null => {
|
||
if (node.name === targetName) {
|
||
return node;
|
||
}
|
||
|
||
for (const child of node.children) {
|
||
const found = findNodeRecursively(child, targetName);
|
||
if (found) {
|
||
return found;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
};
|
||
|
||
const dialogNode = findNodeRecursively(scene, nodeName);
|
||
if (dialogNode) {
|
||
const dialogComponent = dialogNode.getComponent(DialogBox);
|
||
if (dialogComponent) {
|
||
DialogBox.sceneDialogBox = dialogComponent;
|
||
DialogBox.dialogNode = dialogNode;
|
||
// 初始状态为隐藏
|
||
dialogNode.active = false;
|
||
}
|
||
}
|
||
}
|
||
}
|