Customize_Ani.ts 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import { Node, Sprite, Tween, tween, UIOpacity, Vec3, view, Widget } from "cc";
  2. import { gui } from "../../core/ui/ui";
  3. //单个节点进场动画,退场动画
  4. export function toggleSlideOutAndBack(t: Node, offset: Vec3, duration: number = 0.5, onFinish?: () => void) {
  5. if (!t['__originPos']) {
  6. t['__originPos'] = t.position.clone(); // 初始位置
  7. t['__isHidden'] = false;
  8. }
  9. const originPos: Vec3 = t['__originPos'];
  10. if (t['__targetHidden'] === undefined) {
  11. t['__targetHidden'] = t['__isHidden'];
  12. }
  13. const isHidden = t['__isHidden'];
  14. const targetHidden = !t['__targetHidden']; // 翻转目标
  15. t['__targetHidden'] = targetHidden; // 标记目标
  16. const targetPos = targetHidden ? originPos.clone().add(offset) : originPos.clone();
  17. // 停止旧动画(彻底打断)
  18. if (t['__slideTween']) {
  19. t['__slideTween'].stop();
  20. t['__slideTween'] = null;
  21. }
  22. const currentPos = t.position.clone();
  23. const posDelta = targetPos.clone().subtract(currentPos).length();
  24. const isReversing = posDelta > 1 && isHidden !== targetHidden;
  25. const actualDuration = isReversing ? duration * 0.4 : duration;
  26. const tweenInst = tween(t)
  27. .to(actualDuration, { position: new Vec3().set(targetPos) }, { easing: "quadInOut" })
  28. .call(() => {
  29. // 动画未被中断才更新最终状态
  30. if (t['__targetHidden'] === targetHidden) {
  31. t['__isHidden'] = targetHidden;
  32. t['__slideTween'] = null;
  33. onFinish?.();
  34. }
  35. });
  36. t['__slideTween'] = tweenInst;
  37. tweenInst.start();
  38. }
  39. //节点下所有子节点进场动画,退场动画
  40. export function toggleSlideOutAndBack_Nodes(t: Node, offset: Vec3, duration: number = 0.5, onFinish?: () => void) {
  41. const children = t.children;
  42. if (children.length === 0) {
  43. onFinish?.();
  44. return;
  45. }
  46. let completedCount = 0;
  47. children.forEach((node) => {
  48. toggleSlideOutAndBack(node, offset, duration, () => {
  49. completedCount++;
  50. if (completedCount === children.length) {
  51. onFinish?.();
  52. }
  53. });
  54. });
  55. }
  56. export function toggleSlideInAndOut(t: Node, duration: number = 0.5, onFinish?: () => void) {
  57. if (!t['__originPos']) {
  58. t['__originPos'] = t.position.clone();
  59. }
  60. console.log(view.getVisibleSize())
  61. const originPos: Vec3 = t['__originPos'];
  62. const showPos = originPos.clone().add(new Vec3(0, 1334, 0));
  63. let uiOpacity = t.getComponent(UIOpacity);
  64. if (!uiOpacity) {
  65. uiOpacity = t.addComponent(UIOpacity);
  66. uiOpacity.opacity = 0;
  67. }
  68. // 初始化状态记录
  69. if (t['__isHidden'] === undefined) {
  70. t['__isHidden'] = true;
  71. }
  72. if (t['__targetHidden'] === undefined) {
  73. t['__targetHidden'] = t['__isHidden'];
  74. }
  75. // 计算新的目标状态(翻转)
  76. const nextTargetHidden = !t['__targetHidden'];
  77. t['__targetHidden'] = nextTargetHidden;
  78. const targetPos = nextTargetHidden ? originPos : showPos;
  79. const targetOpacity = nextTargetHidden ? 0 : 255;
  80. // 当前状态
  81. const currentPos = t.position.clone();
  82. const currentOpacity = uiOpacity.opacity;
  83. const posDiff = targetPos.clone().subtract(currentPos).length();
  84. const isMidTween = currentOpacity > 0 && currentOpacity < 255 && posDiff > 0;
  85. const actualDuration = isMidTween ? duration * 0.4 : duration;
  86. // 停止旧动画
  87. Tween.stopAllByTarget(t);
  88. Tween.stopAllByTarget(uiOpacity);
  89. tween(t)
  90. .parallel(
  91. tween(t).to(actualDuration, { position: targetPos.clone() }, { easing: 'quadInOut' }),
  92. tween(uiOpacity).to(actualDuration, { opacity: targetOpacity }, { easing: 'quadInOut' })
  93. )
  94. .call(() => {
  95. // 只有目标状态未被再次更改时,才确认最终状态
  96. if (t['__targetHidden'] === nextTargetHidden) {
  97. t['__isHidden'] = nextTargetHidden;
  98. onFinish?.();
  99. }
  100. })
  101. .start();
  102. }
  103. //呼吸动画
  104. export function toggleBreath(t: Node, duration: number = 0.5, onFinish?: () => void) {
  105. if (!t['__originalScale']) {
  106. t['__originalScale'] = t.scale.clone(); // 缓存初始缩放
  107. }
  108. const originalScale: Vec3 = t['__originalScale'];
  109. //停止之前的 tween,并还原缩放
  110. if (t['__breathTween']) {
  111. t['__breathTween'].stop();
  112. t.scale = originalScale.clone(); //强制还原到初始 scale
  113. }
  114. const scaleUp = originalScale.clone().multiplyScalar(1.1);
  115. const scaleDown = originalScale;
  116. const breathTween = tween(t)
  117. .repeatForever(
  118. tween(t)
  119. .to(duration, { scale: scaleUp }, { easing: 'sineInOut' })
  120. .to(duration, { scale: scaleDown }, { easing: 'sineInOut' })
  121. .call(() => {
  122. console.log('breath');
  123. if (onFinish) onFinish();
  124. })
  125. );
  126. t['__breathTween'] = breathTween;
  127. breathTween.start();
  128. }
  129. //提醒动画
  130. export function toggleRemind(t: Node, duration: number = 0.2, delay: number = 1.5, onFinish?: () => void) {
  131. if (!t['__originScale']) {
  132. t['__originScale'] = t.scale.clone(); // 记录初始缩放
  133. }
  134. if (t['__remindTween']) {
  135. t['__remindTween'].stop();
  136. }
  137. const originalScale = t['__originScale'];
  138. const scaleUp = originalScale.clone().multiplyScalar(1.2);
  139. const remindTween = tween(t)
  140. .repeatForever(
  141. tween()
  142. .to(duration, { scale: scaleUp }, { easing: 'sineOut' })
  143. .to(duration, { scale: originalScale }, { easing: 'sineIn' })
  144. .delay(delay)
  145. .call(() => {
  146. if (onFinish) onFinish();
  147. })
  148. );
  149. t['__remindTween'] = remindTween;
  150. remindTween.start();
  151. }
  152. //平移至指定点位动画
  153. export function toggleMoveTo(t: Node, toPos?: Vec3, duration: number = 0.5, onFinish?: () => void) {
  154. if (!t['__originPos']) {
  155. t['__originPos'] = t.position.clone();
  156. }
  157. if (t['__moveTween']) {
  158. t['__moveTween'].stop();
  159. t['__moveTween'] = null;
  160. }
  161. // 没传目标位置时表示还原
  162. const targetPos = toPos ? toPos : t['__originPos'];
  163. const moveTween = tween(t)
  164. .to(duration, { position: targetPos }, { easing: 'quadOut' })
  165. .call(() => {
  166. if (onFinish) onFinish();
  167. });
  168. t['__moveTween'] = moveTween;
  169. moveTween.start();
  170. }
  171. // 根据位移距离进行平移
  172. export function toggleMoveBy(t: Node, offset: Vec3, duration: number = 0.5, onFinish?: () => void) {
  173. if (!t['__originPos']) {
  174. t['__originPos'] = t.position.clone();
  175. }
  176. if (t['__moveTween']) {
  177. t['__moveTween'].stop();
  178. t['__moveTween'] = null;
  179. }
  180. const originPos: Vec3 = t['__originPos'];
  181. const targetPos = t.position.equals(originPos) ? originPos.clone().add(offset) : originPos;
  182. t.position = targetPos;
  183. const moveTween = tween(t)
  184. .to(duration, { position: targetPos }, { easing: "quadInOut" })
  185. .call(() => {
  186. if (onFinish) onFinish();
  187. });
  188. t['__moveTween'] = moveTween;
  189. moveTween.start();
  190. }
  191. //图片填充动画 未完善
  192. export function fillImage(t: Node, duration: number, target: number, onFinish?: Function) {
  193. let sprite = t.getComponent(Sprite);
  194. tween(sprite)
  195. .to(duration, { fillRange: target }, { easing: 'quadOut' })
  196. .start();
  197. }
  198. //弹窗动画
  199. export function ani_ui(node: Node, end: number = 1.0): void {
  200. gui.scale_elasticOut_anim(node, 1.2, 0.5, end);
  201. }