UITween.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { TweenEasing } from "./TweenEasing";
  2. const { ccclass, property, executeInEditMode, playOnFocus } = cc._decorator;
  3. /**
  4. * 缓动数据
  5. * @author 薛鸿潇
  6. */
  7. @ccclass("PropsData")
  8. export class PropsData {
  9. @property({ displayName: "坐标" })
  10. public position: cc.Vec2 = cc.Vec2.ZERO;
  11. @property({ displayName: "旋转" })
  12. public angle: number = 0;
  13. @property({ displayName: "缩放" })
  14. public scale: number = 1;
  15. }
  16. /**
  17. * 缓动数据
  18. * @author 薛鸿潇
  19. */
  20. @ccclass("TweenData")
  21. export class TweenData {
  22. @property({ displayName: "属性数据", type: PropsData })
  23. public propsData: PropsData = new PropsData();
  24. @property({ displayName: "持续时间", tooltip: '单位:秒' })
  25. public duration: number = 0.3;
  26. @property({ displayName: "延迟时间", tooltip: '单位:秒' })
  27. public delay: number = 0;
  28. @property({ displayName: "是否为差值" })
  29. public isOffset: boolean = false;
  30. @property({ displayName: '完成回调', tooltip: "缓动完成时触发", type: cc.Component.EventHandler })
  31. public onComplete: cc.Component.EventHandler = new cc.Component.EventHandler();
  32. @property({ displayName: '开始回调', tooltip: "缓动启动时触发", type: cc.Component.EventHandler })
  33. public onStart: cc.Component.EventHandler = new cc.Component.EventHandler();
  34. @property({ displayName: '过程回调', tooltip: "缓动更新时触发", type: cc.Component.EventHandler })
  35. public onUpdate: cc.Component.EventHandler = new cc.Component.EventHandler();
  36. }
  37. /**
  38. * 缓动组件,目前只支持节点的三个基础属性
  39. * @author 薛鸿潇
  40. */
  41. @ccclass
  42. @executeInEditMode()
  43. @playOnFocus()
  44. export class UITween extends cc.Component {
  45. @property({ displayName: "节点初始属性", type: PropsData })
  46. public fromData: PropsData = new PropsData();
  47. @property({ displayName: "节点目标属性", type: TweenData })
  48. public targetData: TweenData[] = [];
  49. @property({ displayName: '缓动节点', tooltip: "要播放缓动动画的目标,不填则是当前节点", type: cc.Node })
  50. public target: cc.Node = null;
  51. @property({ displayName: "缓动效果", type: cc.Enum(TweenEasing) })
  52. public tweenEasing: TweenEasing = TweenEasing.linear;
  53. @property({ displayName: '执行次数', tooltip: "为0则一直重复执行" })
  54. public repeat: number = 1;
  55. @property({ displayName: "单次完成回调", tooltip: '单次缓动序列执行完毕时触发', type: cc.Component.EventHandler })
  56. public onOneRepeatComplete: cc.Component.EventHandler = new cc.Component.EventHandler();
  57. @property({ displayName: "自动播放" })
  58. public playOnLoad: boolean = false;
  59. private _edit_play: boolean = false;
  60. @property({ displayName: "编辑器预览" })
  61. get edit_play(): boolean {
  62. if (this._edit_play && !this.edit_playing) {
  63. this.edit_playing = true;
  64. this.play();
  65. }
  66. return this._edit_play;
  67. }
  68. set edit_play(v: boolean) {
  69. this._edit_play = v;
  70. if (this.target && v) {
  71. this.target.stopAllActions();
  72. }
  73. if (!v) this.edit_playing = false;
  74. }
  75. /** 编辑器是否正在播放 */
  76. private edit_playing = false;
  77. public start() {
  78. !this.target && (this.target = this.node);
  79. this.target = this.target || this.node;
  80. this.currentRepeat = this.repeat;
  81. if (CC_EDITOR) {
  82. this._edit_play && this.play();
  83. } else {
  84. this.playOnLoad && this.play();
  85. }
  86. }
  87. private t: cc.Tween<any>;
  88. /** 剩余播放次数 */
  89. private currentRepeat: number;
  90. public play() {
  91. if (CC_EDITOR && !this.edit_playing) return;
  92. if (this.repeat == 0) {
  93. this.t = cc.tween(this.target);
  94. this.readyPlay();
  95. this.t = this.t.call(this.play.bind(this));
  96. this.t.start();
  97. }
  98. else if (this.currentRepeat) {
  99. this.t = cc.tween(this.target);
  100. this.readyPlay();
  101. this.t = this.t.call(this.play.bind(this));
  102. this.currentRepeat--;
  103. this.t.start();
  104. }
  105. else {
  106. this.t = null;
  107. }
  108. }
  109. public readyPlay() {
  110. let self = this;
  111. if (this.fromData) {
  112. this.node.setPosition(this.fromData.position.clone());
  113. this.node.scale = this.fromData.scale;
  114. this.node.angle = this.fromData.angle;
  115. }
  116. if (this.targetData.length) {
  117. for (let i = 0; i < this.targetData.length; i++) {
  118. const targetData = this.targetData[i];
  119. if (targetData.delay != 0) {
  120. this.t = this.t.delay(targetData.delay);
  121. }
  122. let toData = {
  123. position: targetData.propsData.position,
  124. scale: targetData.propsData.scale,
  125. angle: targetData.propsData.angle
  126. }
  127. if (targetData.isOffset) {
  128. toData.position = this.node.getPosition().add(targetData.propsData.position);
  129. toData.scale = this.node.scale + targetData.propsData.scale;
  130. toData.angle = this.node.angle + targetData.propsData.angle;
  131. }
  132. this.t = this.t.to(
  133. targetData.duration,
  134. toData,
  135. {
  136. easing: TweenEasing[this.tweenEasing] as any,
  137. onComplete: (target: cc.Node) => {
  138. targetData.onComplete && targetData.onComplete.emit([target]);
  139. this.edit_playing = false;
  140. this._edit_play = false;
  141. },
  142. onStart: (target: cc.Node) => { targetData.onStart && targetData.onStart.emit([target]) },
  143. onUpdate: (target: cc.Node, ratio: number) => { targetData.onUpdate && targetData.onUpdate.emit([target, ratio]) }
  144. }
  145. );
  146. }
  147. }
  148. if (this.onOneRepeatComplete) {
  149. this.t = this.t.call(() => {
  150. self.onOneRepeatComplete.emit([this, this.t])
  151. });
  152. }
  153. }
  154. }