| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- 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<Node, Vec3> = new Map();
-
- // 存储正在播放动画的按钮
- private animatingButtons: Set<Node> = 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();
- }
- }
|