TipItem.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { _decorator, Component, Node, Label, tween, Vec3, UIOpacity, Tween } from 'cc';
  2. import { Tips } from './Tips';
  3. const { ccclass, property } = _decorator;
  4. @ccclass('TipItem')
  5. export class TipItem extends Component {
  6. @property(Label)
  7. contentLabel: Label = null!;
  8. private isInUse: boolean = false;
  9. onLoad() {
  10. // 查找子节点中的Label组件
  11. if (!this.contentLabel) {
  12. const contentNode = this.node.getChildByName('contentLabel');
  13. if (contentNode) {
  14. this.contentLabel = contentNode.getComponent(Label);
  15. }
  16. }
  17. }
  18. /**
  19. * 初始化提示项
  20. */
  21. public init(content: string, startPos: Vec3): void {
  22. this.isInUse = true;
  23. // 设置文字内容
  24. if (this.contentLabel) {
  25. this.contentLabel.string = content;
  26. }
  27. // 设置初始位置和透明度
  28. this.node.setPosition(startPos);
  29. this.node.active = true;
  30. // 重置透明度
  31. const uiOpacity = this.node.getComponent(UIOpacity);
  32. if (uiOpacity) {
  33. uiOpacity.opacity = 255;
  34. }
  35. // 重置缩放
  36. this.node.setScale(Vec3.ONE);
  37. // 开始动画
  38. this.playAnimation();
  39. }
  40. /**
  41. * 播放飘字动画
  42. */
  43. private playAnimation(): void {
  44. const duration = 2.0; // 总动画时长
  45. const moveDistance = 100; // 向上移动距离
  46. // 获取当前位置
  47. const startPos = this.node.getPosition();
  48. const endPos = new Vec3(startPos.x, startPos.y + moveDistance, startPos.z);
  49. // 确保节点有UIOpacity组件
  50. let uiOpacity = this.node.getComponent(UIOpacity);
  51. if (!uiOpacity) {
  52. uiOpacity = this.node.addComponent(UIOpacity);
  53. }
  54. // 创建动画序列
  55. tween(this.node)
  56. .parallel(
  57. // 位置动画:向上移动
  58. tween().to(duration, { position: endPos }, { easing: 'quartOut' }),
  59. // 缩放动画:先放大再缩小
  60. tween().delay(duration * 0.1).to(duration * 0.3, {
  61. scale: new Vec3(1.1, 1.1, 1)
  62. }).to(duration * 0.6, {
  63. scale: new Vec3(0.9, 0.9, 1)
  64. }),
  65. // 透明度渐变
  66. tween().delay(duration * 0.5).call(() => {
  67. if (uiOpacity && uiOpacity.isValid) {
  68. tween(uiOpacity)
  69. .to(duration * 0.5, { opacity: 0 })
  70. .start();
  71. }
  72. })
  73. )
  74. .call(() => {
  75. // 动画结束,回收到对象池
  76. this.recycle();
  77. })
  78. .start();
  79. }
  80. /**
  81. * 回收到对象池
  82. */
  83. private recycle(): void {
  84. this.isInUse = false;
  85. this.node.active = false;
  86. // 停止所有相关的tween动画
  87. Tween.stopAllByTarget(this.node);
  88. // 重置节点状态,确保下次复用时状态正确
  89. // 注意:不重置位置,因为新的位置会在init时设置
  90. this.node.setScale(Vec3.ONE);
  91. // 重置透明度
  92. const uiOpacity = this.node.getComponent(UIOpacity);
  93. if (uiOpacity) {
  94. Tween.stopAllByTarget(uiOpacity);
  95. uiOpacity.opacity = 255;
  96. }
  97. // 清空文字内容
  98. if (this.contentLabel) {
  99. this.contentLabel.string = '';
  100. }
  101. Tips.ins.recycleTipItem(this.node);
  102. }
  103. /**
  104. * 检查是否正在使用
  105. */
  106. public getIsInUse(): boolean {
  107. return this.isInUse;
  108. }
  109. /**
  110. * 强制停止动画并回收
  111. */
  112. public forceRecycle(): void {
  113. Tween.stopAllByTarget(this.node);
  114. this.recycle();
  115. }
  116. }