import { _decorator, Component, Node, tween, Tween, Vec3, UIOpacity } from 'cc'; const { ccclass, property } = _decorator; /** * MenuAni * 菜单动画控制器 * 提供菜单相关的动画效果 */ @ccclass('MenuAni') export class MenuAni extends Component { /** 动画持续时间 */ @property({ tooltip: '动画持续时间(秒)' }) public animationDuration: number = 0.3; /** 淡入淡出动画的目标节点 */ @property({ type: Node, tooltip: '执行淡入淡出动画的目标节点' }) public fadeTarget: Node = null; /** 缩放动画的目标节点 */ @property({ type: Node, tooltip: '执行缩放动画的目标节点' }) public scaleTarget: Node = null; private _originalScale: Vec3 = new Vec3(); onLoad() { // 保存原始缩放值 if (this.scaleTarget) { this._originalScale.set(this.scaleTarget.scale); } } /** * 淡入动画 * @param target 目标节点,如果不指定则使用fadeTarget * @param duration 动画时长,如果不指定则使用animationDuration */ public fadeIn(target?: Node, duration?: number): Promise { const animTarget = target || this.fadeTarget; const animDuration = duration !== undefined ? duration : this.animationDuration; if (!animTarget) { console.warn('[MenuAni] fadeIn: 未指定目标节点'); return Promise.resolve(); } return new Promise((resolve) => { // 获取UIOpacity组件 let uiOpacity = animTarget.getComponent(UIOpacity); if (!uiOpacity) { console.warn(`[MenuAni] fadeIn: 节点 ${animTarget.name} 缺少UIOpacity组件,请手动添加`); return; } // 停止现有动画 Tween.stopAllByTarget(uiOpacity); // 设置初始透明度 uiOpacity.opacity = 0; // 执行淡入动画 tween(uiOpacity) .to(animDuration, { opacity: 255 }, { easing: 'quadOut' }) .call(() => { resolve(); }) .start(); }); } /** * 淡出动画 * @param target 目标节点,如果不指定则使用fadeTarget * @param duration 动画时长,如果不指定则使用animationDuration */ public fadeOut(target?: Node, duration?: number): Promise { const animTarget = target || this.fadeTarget; const animDuration = duration !== undefined ? duration : this.animationDuration; if (!animTarget) { console.warn('[MenuAni] fadeOut: 未指定目标节点'); return Promise.resolve(); } return new Promise((resolve) => { // 获取UIOpacity组件 let uiOpacity = animTarget.getComponent(UIOpacity); if (!uiOpacity) { console.warn(`[MenuAni] fadeOut: 节点 ${animTarget.name} 缺少UIOpacity组件,请手动添加`); return; } // 停止现有动画 Tween.stopAllByTarget(uiOpacity); // 执行淡出动画 tween(uiOpacity) .to(animDuration, { opacity: 0 }, { easing: 'quadIn' }) .call(() => { resolve(); }) .start(); }); } /** * 缩放弹出动画 * @param target 目标节点,如果不指定则使用scaleTarget * @param duration 动画时长,如果不指定则使用animationDuration */ public scalePopIn(target?: Node, duration?: number): Promise { const animTarget = target || this.scaleTarget; const animDuration = duration !== undefined ? duration : this.animationDuration; if (!animTarget) { console.warn('[MenuAni] scalePopIn: 未指定目标节点'); return Promise.resolve(); } return new Promise((resolve) => { // 停止现有动画 Tween.stopAllByTarget(animTarget); // 设置初始缩放 animTarget.setScale(0, 0, 1); // 执行弹出动画 tween(animTarget) .to(animDuration, { scale: this._originalScale }, { easing: 'backOut' }) .call(() => { resolve(); }) .start(); }); } /** * 缩放收缩动画 * @param target 目标节点,如果不指定则使用scaleTarget * @param duration 动画时长,如果不指定则使用animationDuration */ public scalePopOut(target?: Node, duration?: number): Promise { const animTarget = target || this.scaleTarget; const animDuration = duration !== undefined ? duration : this.animationDuration; if (!animTarget) { console.warn('[MenuAni] scalePopOut: 未指定目标节点'); return Promise.resolve(); } return new Promise((resolve) => { // 停止现有动画 Tween.stopAllByTarget(animTarget); // 执行收缩动画 tween(animTarget) .to(animDuration, { scale: new Vec3(0, 0, 1) }, { easing: 'backIn' }) .call(() => { resolve(); }) .start(); }); } /** * 组合动画:淡入 + 缩放弹出 * @param fadeTarget 淡入动画目标节点 * @param scaleTarget 缩放动画目标节点 * @param duration 动画时长 */ public async fadeInWithScale(fadeTarget?: Node, scaleTarget?: Node, duration?: number): Promise { const promises: Promise[] = []; if (fadeTarget || this.fadeTarget) { promises.push(this.fadeIn(fadeTarget, duration)); } if (scaleTarget || this.scaleTarget) { promises.push(this.scalePopIn(scaleTarget, duration)); } await Promise.all(promises); } /** * 组合动画:淡出 + 缩放收缩 * @param fadeTarget 淡出动画目标节点 * @param scaleTarget 缩放动画目标节点 * @param duration 动画时长 */ public async fadeOutWithScale(fadeTarget?: Node, scaleTarget?: Node, duration?: number): Promise { const promises: Promise[] = []; if (fadeTarget || this.fadeTarget) { promises.push(this.fadeOut(fadeTarget, duration)); } if (scaleTarget || this.scaleTarget) { promises.push(this.scalePopOut(scaleTarget, duration)); } await Promise.all(promises); } /** * 停止所有动画 */ public stopAllAnimations() { if (this.fadeTarget) { const uiOpacity = this.fadeTarget.getComponent(UIOpacity); if (uiOpacity) { Tween.stopAllByTarget(uiOpacity); } } if (this.scaleTarget) { Tween.stopAllByTarget(this.scaleTarget); } } /** * 重置所有目标节点到初始状态 */ public resetToInitialState() { this.stopAllAnimations(); if (this.fadeTarget) { let uiOpacity = this.fadeTarget.getComponent(UIOpacity); if (uiOpacity) { uiOpacity.opacity = 255; } } if (this.scaleTarget) { this.scaleTarget.setScale(this._originalScale); } } }