PopUPAni.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import { _decorator, Component, Node, Tween, tween, Vec3 } from 'cc';
  2. const { ccclass, property } = _decorator;
  3. /**
  4. * 通用弹出动画控制器
  5. * 负责管理UI面板的显示和隐藏动画
  6. */
  7. @ccclass('PopUPAni')
  8. export class PopUPAni extends Component {
  9. // Gray节点引用
  10. @property(Node) grayNode: Node = null;
  11. /**
  12. * 显示面板动画
  13. * 面板从小到大的缩放动画,从屏幕中央弹出
  14. */
  15. public showPanel(): Promise<void> {
  16. return new Promise((resolve) => {
  17. // 设置Gray节点为true
  18. if (this.grayNode) {
  19. this.grayNode.active = true;
  20. }
  21. // 设置初始状态:位置居中,缩小到0
  22. this.node.setPosition(0, 0, 0); // 先移动到屏幕正中间
  23. this.node.setScale(Vec3.ZERO); // 然后设置缩放为0
  24. // 缩放动画:从0放大到1
  25. tween(this.node)
  26. .to(0.3, { scale: new Vec3(1, 1, 1) }, {
  27. easing: 'backOut' // 使用回弹缓动效果
  28. })
  29. .call(() => {
  30. resolve();
  31. })
  32. .start();
  33. });
  34. }
  35. /**
  36. * 隐藏面板动画
  37. * 面板从大到小的缩放动画,然后回到原来的画面外右侧位置并恢复原来大小
  38. * 注意:不操作面板的active状态,面板始终保持active=true
  39. */
  40. public hidePanel(): Promise<void> {
  41. return new Promise((resolve) => {
  42. // 缩放动画:从1缩小到0
  43. tween(this.node)
  44. .to(0.2, { scale: Vec3.ZERO }, {
  45. easing: 'backIn' // 使用回弹缓动效果
  46. })
  47. .call(() => {
  48. // 设置Gray节点为false
  49. if (this.grayNode) {
  50. this.grayNode.active = false;
  51. }
  52. // 面板缩放到0后,回到原来的画面外右侧位置并恢复原来大小
  53. // 假设原来位置在画面外右侧,这里设置一个合理的右侧位置
  54. this.node.setPosition(1000, 0, 0); // 移动到画面外右侧
  55. this.node.setScale(Vec3.ONE); // 恢复原来大小
  56. // 不操作 this.node.active,保持面板原有的active状态
  57. resolve();
  58. })
  59. .start();
  60. });
  61. }
  62. /**
  63. * 立即隐藏面板(无动画)
  64. * 注意:不操作面板的active状态,面板始终保持active=true
  65. */
  66. public hidePanelImmediate(): void {
  67. // 设置Gray节点为false
  68. if (this.grayNode) {
  69. this.grayNode.active = false;
  70. }
  71. this.node.setScale(Vec3.ZERO);
  72. // 立即移动到画面外右侧位置并恢复原来大小
  73. this.node.setPosition(1000, 0, 0); // 移动到画面外右侧
  74. this.node.setScale(Vec3.ONE); // 恢复原来大小
  75. // 不操作 this.node.active,保持面板原有的active状态
  76. }
  77. /**
  78. * 立即显示面板(无动画)
  79. * 注意:不操作面板的active状态,面板始终保持active=true
  80. */
  81. public showPanelImmediate(): void {
  82. // 设置Gray节点为true
  83. if (this.grayNode) {
  84. this.grayNode.active = true;
  85. }
  86. this.node.setPosition(0, 0, 0); // 先移动到屏幕正中间
  87. this.node.setScale(Vec3.ONE); // 然后设置缩放为1
  88. // 不操作 this.node.active,保持面板原有的active状态
  89. }
  90. /**
  91. * 图标升级成功动画
  92. * 播放图标的放大缩小动画
  93. * @param iconNode 图标节点
  94. */
  95. public playIconUpgradeAnimation(iconNode: Node): Promise<void> {
  96. return new Promise((resolve) => {
  97. if (!iconNode) {
  98. resolve();
  99. return;
  100. }
  101. // 停止之前的动画,防止快速点击时动画冲突
  102. Tween.stopAllByTarget(iconNode);
  103. // 重置到原始缩放状态,确保动画从正确的状态开始
  104. iconNode.setScale(Vec3.ONE);
  105. // 保存原始缩放值
  106. const originalScale = Vec3.ONE.clone();
  107. // 创建缩放动画:放大到1.5倍再立即缩小回原始大小
  108. const scaleAnimation = tween(iconNode)
  109. .to(0.25, { scale: new Vec3(originalScale.x * 1.5, originalScale.y * 1.5, originalScale.z) }, {
  110. easing: 'sineOut'
  111. })
  112. .to(0.25, { scale: originalScale }, {
  113. easing: 'sineIn'
  114. });
  115. // 只播放缩放动画
  116. scaleAnimation.call(() => resolve()).start();
  117. });
  118. }
  119. /**
  120. * 提示图标动画
  121. * 播放缩小到0.7倍再放大到原来大小的重复动画,直到条件不满足为止
  122. * @param iconNode 图标节点
  123. * @param checkCondition 检查条件的回调函数,返回false时停止动画
  124. */
  125. public playIconTipAnimation(iconNode: Node, checkCondition?: () => boolean): void {
  126. if (!iconNode) return;
  127. // 停止之前的动画
  128. Tween.stopAllByTarget(iconNode);
  129. // 保存原始缩放值
  130. const originalScale = iconNode.scale.clone();
  131. // 如果没有提供检查条件,使用默认的无限重复动画
  132. if (!checkCondition) {
  133. const scaleAnimation = tween(iconNode)
  134. .to(0.5, { scale: new Vec3(originalScale.x * 0.7, originalScale.y * 0.7, originalScale.z) }, {
  135. easing: 'sineInOut'
  136. })
  137. .to(0.5, { scale: originalScale }, {
  138. easing: 'sineInOut'
  139. })
  140. .repeatForever(); // 无限重复
  141. scaleAnimation.start();
  142. return;
  143. }
  144. // 带条件检查的动画循环
  145. const playAnimationCycle = () => {
  146. // 在每次动画循环开始前检查条件
  147. if (!checkCondition()) {
  148. // 条件不满足,停止动画并恢复原始缩放
  149. this.stopIconTipAnimation(iconNode);
  150. return;
  151. }
  152. // 创建一次完整的缩放动画循环
  153. const scaleAnimation = tween(iconNode)
  154. .to(0.5, { scale: new Vec3(originalScale.x * 0.7, originalScale.y * 0.7, originalScale.z) }, {
  155. easing: 'sineInOut'
  156. })
  157. .to(0.5, { scale: originalScale }, {
  158. easing: 'sineInOut'
  159. })
  160. .call(() => {
  161. // 一次循环完成后,递归调用下一次循环
  162. playAnimationCycle();
  163. });
  164. scaleAnimation.start();
  165. };
  166. // 开始第一次动画循环
  167. playAnimationCycle();
  168. }
  169. /**
  170. * 停止提示图标动画
  171. * @param iconNode 图标节点
  172. */
  173. public stopIconTipAnimation(iconNode: Node): void {
  174. if (!iconNode) return;
  175. // 停止所有动画
  176. Tween.stopAllByTarget(iconNode);
  177. // 恢复原始缩放
  178. iconNode.setScale(Vec3.ONE);
  179. }
  180. }