MenuAni.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import { _decorator, Component, Node, tween, Tween, Vec3, UIOpacity } from 'cc';
  2. const { ccclass, property } = _decorator;
  3. /**
  4. * MenuAni
  5. * 菜单动画控制器
  6. * 提供菜单相关的动画效果
  7. */
  8. @ccclass('MenuAni')
  9. export class MenuAni extends Component {
  10. /** 动画持续时间 */
  11. @property({ tooltip: '动画持续时间(秒)' })
  12. public animationDuration: number = 0.3;
  13. /** 淡入淡出动画的目标节点 */
  14. @property({ type: Node, tooltip: '执行淡入淡出动画的目标节点' })
  15. public fadeTarget: Node = null;
  16. /** 缩放动画的目标节点 */
  17. @property({ type: Node, tooltip: '执行缩放动画的目标节点' })
  18. public scaleTarget: Node = null;
  19. private _originalScale: Vec3 = new Vec3();
  20. onLoad() {
  21. // 保存原始缩放值
  22. if (this.scaleTarget) {
  23. this._originalScale.set(this.scaleTarget.scale);
  24. }
  25. }
  26. /**
  27. * 淡入动画
  28. * @param target 目标节点,如果不指定则使用fadeTarget
  29. * @param duration 动画时长,如果不指定则使用animationDuration
  30. */
  31. public fadeIn(target?: Node, duration?: number): Promise<void> {
  32. const animTarget = target || this.fadeTarget;
  33. const animDuration = duration !== undefined ? duration : this.animationDuration;
  34. if (!animTarget) {
  35. console.warn('[MenuAni] fadeIn: 未指定目标节点');
  36. return Promise.resolve();
  37. }
  38. return new Promise<void>((resolve) => {
  39. // 获取UIOpacity组件
  40. let uiOpacity = animTarget.getComponent(UIOpacity);
  41. if (!uiOpacity) {
  42. console.warn(`[MenuAni] fadeIn: 节点 ${animTarget.name} 缺少UIOpacity组件,请手动添加`);
  43. return;
  44. }
  45. // 停止现有动画
  46. Tween.stopAllByTarget(uiOpacity);
  47. // 设置初始透明度
  48. uiOpacity.opacity = 0;
  49. // 执行淡入动画
  50. tween(uiOpacity)
  51. .to(animDuration, { opacity: 255 }, { easing: 'quadOut' })
  52. .call(() => {
  53. resolve();
  54. })
  55. .start();
  56. });
  57. }
  58. /**
  59. * 淡出动画
  60. * @param target 目标节点,如果不指定则使用fadeTarget
  61. * @param duration 动画时长,如果不指定则使用animationDuration
  62. */
  63. public fadeOut(target?: Node, duration?: number): Promise<void> {
  64. const animTarget = target || this.fadeTarget;
  65. const animDuration = duration !== undefined ? duration : this.animationDuration;
  66. if (!animTarget) {
  67. console.warn('[MenuAni] fadeOut: 未指定目标节点');
  68. return Promise.resolve();
  69. }
  70. return new Promise<void>((resolve) => {
  71. // 获取UIOpacity组件
  72. let uiOpacity = animTarget.getComponent(UIOpacity);
  73. if (!uiOpacity) {
  74. console.warn(`[MenuAni] fadeOut: 节点 ${animTarget.name} 缺少UIOpacity组件,请手动添加`);
  75. return;
  76. }
  77. // 停止现有动画
  78. Tween.stopAllByTarget(uiOpacity);
  79. // 执行淡出动画
  80. tween(uiOpacity)
  81. .to(animDuration, { opacity: 0 }, { easing: 'quadIn' })
  82. .call(() => {
  83. resolve();
  84. })
  85. .start();
  86. });
  87. }
  88. /**
  89. * 缩放弹出动画
  90. * @param target 目标节点,如果不指定则使用scaleTarget
  91. * @param duration 动画时长,如果不指定则使用animationDuration
  92. */
  93. public scalePopIn(target?: Node, duration?: number): Promise<void> {
  94. const animTarget = target || this.scaleTarget;
  95. const animDuration = duration !== undefined ? duration : this.animationDuration;
  96. if (!animTarget) {
  97. console.warn('[MenuAni] scalePopIn: 未指定目标节点');
  98. return Promise.resolve();
  99. }
  100. return new Promise<void>((resolve) => {
  101. // 停止现有动画
  102. Tween.stopAllByTarget(animTarget);
  103. // 设置初始缩放
  104. animTarget.setScale(0, 0, 1);
  105. // 执行弹出动画
  106. tween(animTarget)
  107. .to(animDuration, { scale: this._originalScale }, { easing: 'backOut' })
  108. .call(() => {
  109. resolve();
  110. })
  111. .start();
  112. });
  113. }
  114. /**
  115. * 缩放收缩动画
  116. * @param target 目标节点,如果不指定则使用scaleTarget
  117. * @param duration 动画时长,如果不指定则使用animationDuration
  118. */
  119. public scalePopOut(target?: Node, duration?: number): Promise<void> {
  120. const animTarget = target || this.scaleTarget;
  121. const animDuration = duration !== undefined ? duration : this.animationDuration;
  122. if (!animTarget) {
  123. console.warn('[MenuAni] scalePopOut: 未指定目标节点');
  124. return Promise.resolve();
  125. }
  126. return new Promise<void>((resolve) => {
  127. // 停止现有动画
  128. Tween.stopAllByTarget(animTarget);
  129. // 执行收缩动画
  130. tween(animTarget)
  131. .to(animDuration, { scale: new Vec3(0, 0, 1) }, { easing: 'backIn' })
  132. .call(() => {
  133. resolve();
  134. })
  135. .start();
  136. });
  137. }
  138. /**
  139. * 组合动画:淡入 + 缩放弹出
  140. * @param fadeTarget 淡入动画目标节点
  141. * @param scaleTarget 缩放动画目标节点
  142. * @param duration 动画时长
  143. */
  144. public async fadeInWithScale(fadeTarget?: Node, scaleTarget?: Node, duration?: number): Promise<void> {
  145. const promises: Promise<void>[] = [];
  146. if (fadeTarget || this.fadeTarget) {
  147. promises.push(this.fadeIn(fadeTarget, duration));
  148. }
  149. if (scaleTarget || this.scaleTarget) {
  150. promises.push(this.scalePopIn(scaleTarget, duration));
  151. }
  152. await Promise.all(promises);
  153. }
  154. /**
  155. * 组合动画:淡出 + 缩放收缩
  156. * @param fadeTarget 淡出动画目标节点
  157. * @param scaleTarget 缩放动画目标节点
  158. * @param duration 动画时长
  159. */
  160. public async fadeOutWithScale(fadeTarget?: Node, scaleTarget?: Node, duration?: number): Promise<void> {
  161. const promises: Promise<void>[] = [];
  162. if (fadeTarget || this.fadeTarget) {
  163. promises.push(this.fadeOut(fadeTarget, duration));
  164. }
  165. if (scaleTarget || this.scaleTarget) {
  166. promises.push(this.scalePopOut(scaleTarget, duration));
  167. }
  168. await Promise.all(promises);
  169. }
  170. /**
  171. * 停止所有动画
  172. */
  173. public stopAllAnimations() {
  174. if (this.fadeTarget) {
  175. const uiOpacity = this.fadeTarget.getComponent(UIOpacity);
  176. if (uiOpacity) {
  177. Tween.stopAllByTarget(uiOpacity);
  178. }
  179. }
  180. if (this.scaleTarget) {
  181. Tween.stopAllByTarget(this.scaleTarget);
  182. }
  183. }
  184. /**
  185. * 重置所有目标节点到初始状态
  186. */
  187. public resetToInitialState() {
  188. this.stopAllAnimations();
  189. if (this.fadeTarget) {
  190. let uiOpacity = this.fadeTarget.getComponent(UIOpacity);
  191. if (uiOpacity) {
  192. uiOpacity.opacity = 255;
  193. }
  194. }
  195. if (this.scaleTarget) {
  196. this.scaleTarget.setScale(this._originalScale);
  197. }
  198. }
  199. }