import { _decorator, Component, Node, Button, Vec3, tween, find } from 'cc'; const { ccclass, property } = _decorator; /** * ButtonAni - 通用按钮按压效果管理器 * 自动为场景中所有按钮添加按压缩放效果 * 挂载在场景的根节点或UI管理节点上 */ @ccclass('ButtonAni') export class ButtonAni extends Component { @property({ tooltip: '按压时的缩放比例' }) public pressScale: number = 0.9; @property({ tooltip: '按压动画时长(秒)' }) public pressDuration: number = 0.1; @property({ tooltip: '释放动画时长(秒)' }) public releaseDuration: number = 0.1; @property({ tooltip: '是否自动查找所有按钮' }) public autoFindButtons: boolean = true; @property({ type: [Node], tooltip: '手动指定的按钮节点列表(如果不自动查找)' }) public manualButtons: Node[] = []; // 存储按钮的原始缩放值 private originalScales: Map = new Map(); // 存储正在播放动画的按钮 private animatingButtons: Set = new Set(); start() { this.initializeButtonEffects(); } /** * 初始化所有按钮的按压效果 */ private initializeButtonEffects() { const buttons = this.getAllButtons(); console.log(`[ButtonAni] 找到 ${buttons.length} 个按钮,开始添加按压效果`); buttons.forEach(buttonNode => { this.addButtonPressEffect(buttonNode); }); } /** * 获取所有需要添加效果的按钮 */ private getAllButtons(): Node[] { if (!this.autoFindButtons) { return this.manualButtons.filter(btn => btn && btn.isValid); } const allButtons: Node[] = []; // 从Canvas开始递归查找所有按钮 const canvas = find('Canvas'); if (canvas) { this.findButtonsRecursively(canvas, allButtons); } // 也查找Canvas-001(如果存在) const canvas001 = find('Canvas-001'); if (canvas001) { this.findButtonsRecursively(canvas001, allButtons); } return allButtons; } /** * 递归查找节点下的所有按钮组件 */ private findButtonsRecursively(node: Node, buttons: Node[]) { if (!node || !node.isValid) return; // 检查当前节点是否有Button组件 const button = node.getComponent(Button); if (button && button.enabled) { buttons.push(node); } // 递归检查子节点 node.children.forEach(child => { this.findButtonsRecursively(child, buttons); }); } /** * 为单个按钮添加按压效果 */ private addButtonPressEffect(buttonNode: Node) { if (!buttonNode || !buttonNode.isValid) return; const button = buttonNode.getComponent(Button); if (!button) return; // 保存原始缩放值 this.originalScales.set(buttonNode, buttonNode.scale.clone()); // 添加按下事件监听 buttonNode.on(Node.EventType.TOUCH_START, this.onButtonPress.bind(this, buttonNode), this); buttonNode.on(Node.EventType.TOUCH_END, this.onButtonRelease.bind(this, buttonNode), this); buttonNode.on(Node.EventType.TOUCH_CANCEL, this.onButtonRelease.bind(this, buttonNode), this); console.log(`[ButtonAni] 已为按钮 ${buttonNode.name} 添加按压效果`); } /** * 按钮按下事件处理 */ private onButtonPress(buttonNode: Node) { if (!buttonNode || !buttonNode.isValid || this.animatingButtons.has(buttonNode)) { return; } this.animatingButtons.add(buttonNode); const originalScale = this.originalScales.get(buttonNode) || Vec3.ONE; const pressedScale = originalScale.clone().multiplyScalar(this.pressScale); // 停止之前的动画 tween(buttonNode).stop(); // 播放按压动画 tween(buttonNode) .to(this.pressDuration, { scale: pressedScale }, { easing: 'quadOut' }) .call(() => { // 动画完成后从集合中移除 this.animatingButtons.delete(buttonNode); }) .start(); } /** * 按钮释放事件处理 */ private onButtonRelease(buttonNode: Node) { if (!buttonNode || !buttonNode.isValid) { return; } this.animatingButtons.add(buttonNode); const originalScale = this.originalScales.get(buttonNode) || Vec3.ONE; // 停止之前的动画 tween(buttonNode).stop(); // 播放释放动画 tween(buttonNode) .to(this.releaseDuration, { scale: originalScale }, { easing: 'backOut' }) .call(() => { // 动画完成后从集合中移除 this.animatingButtons.delete(buttonNode); }) .start(); } /** * 手动刷新按钮效果(当有新按钮动态创建时调用) */ public refreshButtonEffects() { // 清理之前的监听 this.cleanup(); // 重新初始化 this.initializeButtonEffects(); } /** * 清理所有按钮监听和动画 */ private cleanup() { this.originalScales.forEach((scale, buttonNode) => { if (buttonNode && buttonNode.isValid) { // 移除事件监听 buttonNode.off(Node.EventType.TOUCH_START, this.onButtonPress, this); buttonNode.off(Node.EventType.TOUCH_END, this.onButtonRelease, this); buttonNode.off(Node.EventType.TOUCH_CANCEL, this.onButtonRelease, this); // 停止动画并恢复原始缩放 tween(buttonNode).stop(); buttonNode.setScale(scale); } }); this.originalScales.clear(); this.animatingButtons.clear(); } onDestroy() { this.cleanup(); } }