QuickTween.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. import { Component, Node, Vec3, tween, Quat, Sprite, Color, math, easing, Camera, ITweenOption, IPunchTweenOption, IShakeTweenOption } from 'cc';
  2. // import { calcPunchData, calcShakeData } from './Util';
  3. //////////////////////
  4. // Transform
  5. //////////////////////
  6. Node.prototype.qtPosition = function(to: Vec3, duration: number, opts?: ITweenOption) {
  7. return tween(this).to(duration, { position: to }, opts);
  8. }
  9. Node.prototype.qtPositionX = function(to: number, duration: number, opts?: ITweenOption) {
  10. const startPos = this.position;
  11. return tween(this).to(duration, { position: new Vec3(to, startPos.y, startPos.z) }, opts);
  12. }
  13. Node.prototype.qtPositionY = function(to: number, duration: number, opts?: ITweenOption) {
  14. const startPos = this.position;
  15. return tween(this).to(duration, { position: new Vec3(startPos.x, to, startPos.z) }, opts);
  16. }
  17. Node.prototype.qtPositionZ = function(to: number, duration: number, opts?: ITweenOption) {
  18. const startPos = this.position;
  19. return tween(this).to(duration, { position: new Vec3(startPos.x, startPos.y, to) }, opts);
  20. }
  21. Node.prototype.qtWorldPosition = function(to: Vec3, duration: number, opts?: ITweenOption) {
  22. return tween(this).to(duration, { worldPosition: to }, opts);
  23. }
  24. Node.prototype.qtWorldPositionX = function(to: number, duration: number, opts?: ITweenOption) {
  25. const startPos = this.worldPosition;
  26. return tween(this).to(duration, { worldPosition: new Vec3(to, startPos.y, startPos.z) }, opts);
  27. }
  28. Node.prototype.qtWorldPositionY = function(to: number, duration: number, opts?: ITweenOption) {
  29. const startPos = this.worldPosition;
  30. return tween(this).to(duration, { worldPosition: new Vec3(startPos.x, to, startPos.z) }, opts);
  31. }
  32. Node.prototype.qtWorldPositionZ = function(to: number, duration: number, opts?: ITweenOption) {
  33. const startPos = this.worldPosition;
  34. return tween(this).to(duration, { worldPosition: new Vec3(startPos.x, startPos.y, to) }, opts);
  35. }
  36. Node.prototype.qtRotation = function(to: Vec3, duration: number, opts?: ITweenOption) {
  37. return tween(this).to(duration, { eulerAngles: to }, opts);
  38. }
  39. Node.prototype.qtRotationQuat = function(to: Quat, duration: number, opts?: ITweenOption) {
  40. return tween(this).to(duration, { rotation: to }, opts);
  41. }
  42. Node.prototype.qtScale = function(to: Vec3|number, duration: number, opts?: ITweenOption) {
  43. let toScale = to;
  44. if (!(to instanceof Vec3)) {
  45. toScale = new Vec3(to, to, to);
  46. }
  47. return tween(this).to(duration, { scale: toScale }, opts);
  48. }
  49. Node.prototype.qtScaleX = function(to: number, duration: number, opts?: ITweenOption) {
  50. const startScale = this.scale;
  51. return tween(this).to(duration, { scale: new Vec3(to, startScale.y, startScale.z) }, opts);
  52. }
  53. Node.prototype.qtScaleY = function(to: number, duration: number, opts?: ITweenOption) {
  54. const startScale = this.scale;
  55. return tween(this).to(duration, { scale: new Vec3(startScale.x, to, startScale.z) }, opts);
  56. }
  57. Node.prototype.qtScaleZ = function(to: number, duration: number, opts?: ITweenOption) {
  58. const startScale = this.scale;
  59. return tween(this).to(duration, { scale: new Vec3(startScale.x, startScale.y, to) }, opts);
  60. }
  61. Node.prototype.qtPunchPosition = function(punch: Vec3, duration: number, opts?: IPunchTweenOption) {
  62. const vibrato = opts?.vibrato ?? 3;
  63. const elasticity = opts?.elasticity ?? 0.5;
  64. const {tos, durations} = calcPunchData(this.position.clone(), punch, duration, vibrato, elasticity);
  65. const punchTween = tween(this);
  66. tos.forEach((to, index) => {
  67. const d = durations[index];
  68. let tweenOpts: ITweenOption|undefined;
  69. if (index === 0) {
  70. tweenOpts = {
  71. onStart: opts.onStart
  72. }
  73. } else if (index === tos.length - 1) {
  74. tweenOpts = {
  75. onComplete: opts.onComplete
  76. }
  77. }
  78. punchTween.then(tween().to(d, {position: to}, tweenOpts));
  79. });
  80. return punchTween.union();
  81. }
  82. Node.prototype.qtPunchRotation = function(punch: Vec3, duration: number, opts?: IPunchTweenOption) {
  83. const vibrato = opts?.vibrato ?? 3;
  84. const elasticity = opts?.elasticity ?? 0.5;
  85. const {tos, durations} = calcPunchData(this.rotation.clone(), punch, duration, vibrato, elasticity);
  86. const punchTween = tween(this);
  87. tos.forEach((to, index) => {
  88. const d = durations[index];
  89. let tweenOpts: ITweenOption|undefined;
  90. if (index === 0) {
  91. tweenOpts = {
  92. onStart: opts.onStart
  93. }
  94. } else if (index === tos.length - 1) {
  95. tweenOpts = {
  96. onComplete: opts.onComplete
  97. }
  98. }
  99. punchTween.then(tween().to(d, {eulerAngles: to}, tweenOpts));
  100. });
  101. return punchTween.union();
  102. }
  103. Node.prototype.qtPunchScale = function(punch: Vec3, duration: number, opts?: IPunchTweenOption) {
  104. const vibrato = opts?.vibrato ?? 3;
  105. const elasticity = opts?.elasticity ?? 0.5;
  106. const {tos, durations} = calcPunchData(this.scale.clone(), punch, duration, vibrato, elasticity);
  107. const punchTween = tween(this);
  108. tos.forEach((to, index) => {
  109. const d = durations[index];
  110. let tweenOpts: ITweenOption|undefined;
  111. if (index === 0) {
  112. tweenOpts = {
  113. onStart: opts.onStart
  114. }
  115. } else if (index === tos.length - 1) {
  116. tweenOpts = {
  117. onComplete: opts.onComplete
  118. }
  119. }
  120. punchTween.then(tween().to(d, {scale: to}, tweenOpts));
  121. });
  122. return punchTween.union();
  123. }
  124. Node.prototype.qtJumpPosition = function(to: Vec3, jumpHeight: number, jumpNum: number, duration: number, opts?: ITweenOption) {
  125. const tweenPos = new Vec3();
  126. const jumpTween = tween(this);
  127. const totalNum = jumpNum * 2;
  128. this.jumpY = 0;
  129. let startPosY = 0;
  130. const yUpTween = tween().to(duration / totalNum, { jumpY: jumpHeight }, {
  131. onStart: (target: Node) => {
  132. startPosY = target.position.y;
  133. target.jumpY = 0;
  134. },
  135. onUpdate: (target: Node, ratio) => {
  136. tweenPos.set(target.position);
  137. tweenPos.y = startPosY + target.jumpY;
  138. target.position = tweenPos;
  139. },
  140. onComplete: (target: Node) => {
  141. target.jumpY = 0;
  142. }, easing: 'quadOut'
  143. }).to(duration / totalNum, { jumpY: jumpHeight }, {
  144. onStart: (target: Node) => {
  145. startPosY = target.position.y;
  146. },
  147. onUpdate: (target: Node, ratio) => {
  148. tweenPos.set(target.position);
  149. tweenPos.y = startPosY - target.jumpY;
  150. target.position = tweenPos;
  151. },
  152. onComplete: (target: Node) => {
  153. target.jumpY = 0;
  154. }, easing: 'quadIn',
  155. }).union().repeat(jumpNum);
  156. this.jumpOffsetY = 0;
  157. let offsetY = 0;
  158. const offsetYTween = tween().to(duration, { jumpOffsetY: to.y - this.position.y }, {
  159. onStart: (target: Node) => {
  160. offsetY = to.y - target.position.y;
  161. target.jumpOffsetY = 0;
  162. },
  163. onUpdate: (target: Node, ratio) => {
  164. const interpOffsetY = easing.quadOut(ratio) * offsetY;
  165. tweenPos.set(target.position);
  166. tweenPos.y += interpOffsetY;
  167. target.position = tweenPos;
  168. },
  169. onComplete: (target: Node) => {
  170. target.jumpOffsetY = 0;
  171. }, easing: 'quadOut'
  172. });
  173. this.jumpX = this.position.x;
  174. this.jumpZ = this.position.z;
  175. const xzTween = tween().to(duration, { jumpX: to.x, jumpZ: to.z }, {
  176. onStart: opts.onStart,
  177. onUpdate: (target: Node, ratio) => {
  178. tweenPos.set(target.position);
  179. tweenPos.x = target.jumpX;
  180. tweenPos.z = target.jumpZ;
  181. target.position = tweenPos;
  182. opts.onUpdate?.();
  183. },
  184. onComplete: (target: Node) => {
  185. // delete target.jumpX;
  186. // delete target.jumpY;
  187. // delete target.jumpZ;
  188. // delete target.jumpOffsetY;
  189. target.jumpX = target.position.x;
  190. target.jumpZ = target.position.z;
  191. opts.onComplete?.();
  192. }
  193. })
  194. jumpTween.parallel(yUpTween, offsetYTween, xzTween);
  195. return jumpTween;
  196. }
  197. Node.prototype.qtShakePosition = function(strength: Vec3|number, duration: number, opts?: IShakeTweenOption) {
  198. const vibrato = opts?.vibrato ?? 10;
  199. const randomness = opts?.randomness ?? 90;
  200. const fadeOut = opts?.fadeOut ?? true;
  201. let toStrength: Vec3;
  202. let vectorBased = false;
  203. if (!(strength instanceof Vec3)) {
  204. toStrength = new Vec3(strength, strength, strength);
  205. } else {
  206. toStrength = strength;
  207. vectorBased = true;
  208. }
  209. const {tos, durations} = calcShakeData(this.position.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
  210. const shakeTween = tween(this);
  211. tos.forEach((to, index)=> {
  212. const d = durations[index];
  213. let tweenOpts: ITweenOption|undefined;
  214. if (index === 0) {
  215. tweenOpts = {
  216. onStart: opts.onStart
  217. }
  218. } else if (index === tos.length - 1) {
  219. tweenOpts = {
  220. onComplete: opts.onComplete
  221. }
  222. }
  223. shakeTween.then(tween().to(d, {position: to}, tweenOpts));
  224. });
  225. return shakeTween.union();
  226. }
  227. Node.prototype.qtShakeRotation = function(strength: Vec3|number, duration: number, opts?: IShakeTweenOption) {
  228. const vibrato = opts?.vibrato ?? 10;
  229. const randomness = opts?.randomness ?? 90;
  230. const fadeOut = opts?.fadeOut ?? true;
  231. let toStrength: Vec3;
  232. let vectorBased = false;
  233. if (!(strength instanceof Vec3)) {
  234. toStrength = new Vec3(strength, strength, strength);
  235. } else {
  236. toStrength = strength;
  237. vectorBased = true;
  238. }
  239. const {tos, durations} = calcShakeData(this.eulerAngles.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
  240. const shakeTween = tween(this);
  241. tos.forEach((to, index)=> {
  242. const d = durations[index];
  243. let tweenOpts: ITweenOption|undefined;
  244. if (index === 0) {
  245. tweenOpts = {
  246. onStart: opts.onStart
  247. }
  248. } else if (index === tos.length - 1) {
  249. tweenOpts = {
  250. onComplete: opts.onComplete
  251. }
  252. }
  253. shakeTween.then(tween().to(d, {eulerAngles: to}, tweenOpts));
  254. });
  255. return shakeTween.union();
  256. }
  257. Node.prototype.qtShakeScale = function(strength: Vec3|number, duration: number, opts?: IShakeTweenOption) {
  258. const vibrato = opts?.vibrato ?? 10;
  259. const randomness = opts?.randomness ?? 90;
  260. const fadeOut = opts?.fadeOut ?? true;
  261. let toStrength: Vec3;
  262. let vectorBased = false;
  263. if (!(strength instanceof Vec3)) {
  264. toStrength = new Vec3(strength, strength, strength);
  265. } else {
  266. toStrength = strength;
  267. vectorBased = true;
  268. }
  269. const {tos, durations} = calcShakeData(this.scale.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
  270. const shakeTween = tween(this);
  271. tos.forEach((to, index)=> {
  272. const d = durations[index];
  273. let tweenOpts: ITweenOption|undefined;
  274. if (index === 0) {
  275. tweenOpts = {
  276. onStart: opts.onStart
  277. }
  278. } else if (index === tos.length - 1) {
  279. tweenOpts = {
  280. onComplete: opts.onComplete
  281. }
  282. }
  283. shakeTween.then(tween().to(d, {scale: to}, tweenOpts));
  284. });
  285. return shakeTween.union();
  286. }
  287. //////////////////////
  288. // Sprite
  289. //////////////////////
  290. // good color lerp
  291. // https://www.alanzucconi.com/2016/01/06/colour-interpolation/
  292. Sprite.prototype.qtColor = function(to: Color, duration: number, opts?: ITweenOption) {
  293. return tween(this).to(duration, { color: to }, opts);
  294. }
  295. Sprite.prototype.qtOpacity = function(to: number, duration: number, opts?: ITweenOption) {
  296. const startColor = this.color.clone();
  297. const tempColor = new Color();
  298. return tween(this).to(duration, { color: new Color(startColor.r, startColor.g, startColor.b, to) }, {
  299. onStart: opts.onStart,
  300. onUpdate: (target: {_val: number}, ratio: number) => {
  301. const lerpA = startColor.a + (to - startColor.a) * ratio
  302. tempColor.set(startColor.r, startColor.g, startColor.b, lerpA);
  303. this.color = tempColor;
  304. opts.onUpdate?.();
  305. },
  306. onComplete: opts.onComplete
  307. });
  308. }
  309. //////////////////////
  310. // Camera
  311. //////////////////////
  312. Camera.prototype.qtShakePosition = function(strength: Vec3|number, duration: number, opts?: IShakeTweenOption) {
  313. const vibrato = opts?.vibrato ?? 10;
  314. const randomness = opts?.randomness ?? 90;
  315. const fadeOut = opts?.fadeOut ?? true;
  316. let toStrength: Vec3;
  317. let vectorBased = false;
  318. if (!(strength instanceof Vec3)) {
  319. toStrength = new Vec3(strength, strength, strength);
  320. } else {
  321. toStrength = strength;
  322. vectorBased = true;
  323. }
  324. const {tos, durations} = calcShakeData(this.node.position.clone(), duration, toStrength, vibrato, randomness, true, vectorBased, fadeOut)
  325. const shakeTween = tween(this.node);
  326. tos.forEach((to, index)=> {
  327. const d = durations[index];
  328. let tweenOpts: ITweenOption|undefined;
  329. if (index === 0) {
  330. tweenOpts = {
  331. onStart: opts.onStart
  332. }
  333. } else if (index === tos.length - 1) {
  334. tweenOpts = {
  335. onComplete: opts.onComplete
  336. }
  337. }
  338. shakeTween.then(tween().to(d, {position: to}, tweenOpts));
  339. });
  340. return shakeTween.union();
  341. }