Source: animate/Tween.js

animate/Tween.js

  1. /**
  2. * Provide timeline playback of movieclip
  3. * @memberof PIXI.animate
  4. * @class Tween
  5. * @constructor
  6. * @param {PIXI.animate.MovieClip} target The target to play
  7. * @param {Object} startProps The starting properties
  8. * @param {Object} endProps The ending properties
  9. * @param {int} startFrame frame number on which to begin tweening
  10. * @param {int} duration Number of frames to tween
  11. * @param {Function} [ease] Ease function to use
  12. */
  13. class Tween {
  14. constructor(target, startProps, endProps, startFrame, duration, ease) {
  15. /**
  16. * target display object
  17. * @name PIXI.animate.Tween#target
  18. * @type {Object}
  19. */
  20. this.target = target;
  21. /**
  22. * properties at the start of the tween
  23. * @type {Object}
  24. * @name PIXI.animate.Tween#startProps
  25. */
  26. this.startProps = startProps;
  27. /**
  28. * properties at the end of the tween, as well as any properties that are set
  29. * instead of tweened
  30. * @type {Object}
  31. * @name PIXI.animate.Tween#endProps
  32. */
  33. this.endProps = {};
  34. /**
  35. * duration of tween in frames. For a keyframe with no tweening, the duration will be 0.
  36. * @type {int}
  37. * @name PIXI.animate.Tween#duration
  38. */
  39. this.duration = duration;
  40. /**
  41. * The frame that the tween starts on
  42. * @type {int}
  43. * @name PIXI.animate.Tween#startFrame
  44. */
  45. this.startFrame = startFrame;
  46. /**
  47. * the frame that the tween ends on
  48. * @type {int}
  49. * @name PIXI.animate.Tween#endFrame
  50. */
  51. this.endFrame = startFrame + duration;
  52. /**
  53. * easing function to use, if any
  54. * @type {Function}
  55. * @name PIXI.animate.Tween#ease
  56. */
  57. this.ease = ease;
  58. /**
  59. * If we don't tween.
  60. * @type {Boolean}
  61. * @name PIXI.animate.Tween#isTweenlessFrame
  62. */
  63. this.isTweenlessFrame = !endProps;
  64. let prop;
  65. if (endProps) {
  66. //make a copy to safely include any unchanged values from the start of the tween
  67. for (prop in endProps) {
  68. this.endProps[prop] = endProps[prop];
  69. }
  70. }
  71. //copy in any starting properties don't change
  72. for (prop in startProps) {
  73. if (!this.endProps.hasOwnProperty(prop)) {
  74. this.endProps[prop] = startProps[prop];
  75. }
  76. }
  77. }
  78. /**
  79. * Set the current frame.
  80. * @method PIXI.animate.Tween#setPosition
  81. * @param {int} currentFrame
  82. */
  83. setPosition(currentFrame) {
  84. //if this is a single frame with no tweening, or at the end of the tween, then
  85. //just speed up the process by setting values
  86. if (currentFrame >= this.endFrame) {
  87. this.setToEnd();
  88. return;
  89. }
  90. if (this.isTweenlessFrame) {
  91. this.setToEnd();
  92. return;
  93. }
  94. let time = (currentFrame - this.startFrame) / this.duration;
  95. if (this.ease) {
  96. time = this.ease(time);
  97. }
  98. let target = this.target;
  99. let startProps = this.startProps;
  100. let endProps = this.endProps;
  101. for (let prop in endProps) {
  102. let lerp = props[prop];
  103. if (lerp) {
  104. setPropFromShorthand(target, prop, lerp(startProps[prop], endProps[prop], time));
  105. } else {
  106. setPropFromShorthand(target, prop, startProps[prop]);
  107. }
  108. }
  109. }
  110. /**
  111. * Set to the end position
  112. * @method PIXI.animate.Tween#setToEnd
  113. */
  114. setToEnd() {
  115. let endProps = this.endProps;
  116. let target = this.target;
  117. for (let prop in endProps) {
  118. setPropFromShorthand(target, prop, endProps[prop]);
  119. }
  120. }
  121. }
  122. //standard tweening
  123. function lerpValue(start, end, t) {
  124. return start + (end - start) * t;
  125. }
  126. const props = {
  127. //position
  128. x: lerpValue,
  129. y: lerpValue,
  130. //scale
  131. sx: lerpValue,
  132. sy: lerpValue,
  133. //skew
  134. kx: lerpValue,
  135. ky: lerpValue,
  136. //rotation
  137. r: lerpRotation,
  138. //alpha
  139. a: lerpValue,
  140. //tinting
  141. // t: lerpColor,
  142. t: null,
  143. //values to be set
  144. v: null, //visible
  145. c: null, //colorTransform
  146. m: null, //mask
  147. g: null //not sure if we'll actually handle graphics this way?
  148. };
  149. //split r, g, b into separate values for tweening
  150. /*function lerpColor(start, end, t)
  151. {
  152. //split start color into components
  153. let sR = start >> 16 & 0xFF;
  154. let sG = start >> 8 & 0xFF;
  155. let sB = start & 0xFF;
  156. //split end color into components
  157. let eR = end >> 16 & 0xFF;
  158. let eG = end >> 8 & 0xFF;
  159. let eB = end & 0xFF;
  160. //lerp red
  161. let r = sR + (eR - sR) * percent;
  162. //clamp red to valid values
  163. if (r < 0)
  164. r = 0;
  165. else if (r > 255)
  166. r = 255;
  167. //lerp green
  168. let g = sG + (eG - sG) * percent;
  169. //clamp green to valid values
  170. if (g < 0)
  171. g = 0;
  172. else if (g > 255)
  173. g = 255;
  174. //lerp blue
  175. let b = sB + (eB - sB) * percent;
  176. //clamp blue to valid values
  177. if (b < 0)
  178. b = 0;
  179. else if (b > 255)
  180. b = 255;
  181. let combined = (r << 16) | (g << 8) | b;
  182. return combined;
  183. }*/
  184. const PI = Math.PI;
  185. const TWO_PI = PI * 2;
  186. //handle 355 -> 5 degrees only going through a 10 degree change instead of
  187. //the long way around
  188. //Math from http://stackoverflow.com/a/2708740
  189. function lerpRotation(start, end, t) {
  190. let difference = Math.abs(end - start);
  191. if (difference > PI) {
  192. // We need to add on to one of the values.
  193. if (end > start) {
  194. // We'll add it on to start...
  195. start += TWO_PI;
  196. } else {
  197. // Add it on to end.
  198. end += PI + TWO_PI;
  199. }
  200. }
  201. // Interpolate it.
  202. let value = (start + ((end - start) * t));
  203. // wrap to 0-2PI
  204. /*if (value >= 0 && value <= TWO_PI)
  205. return value;
  206. return value % TWO_PI;*/
  207. //just return, as it's faster
  208. return value;
  209. }
  210. function setPropFromShorthand(target, prop, value) {
  211. switch (prop) {
  212. case "x":
  213. target.transform.position.x = value;
  214. break;
  215. case "y":
  216. target.transform.position.y = value;
  217. break;
  218. case "sx":
  219. target.transform.scale.x = value;
  220. break;
  221. case "sy":
  222. target.transform.scale.y = value;
  223. break;
  224. case "kx":
  225. target.transform.skew.x = value;
  226. break;
  227. case "ky":
  228. target.transform.skew.y = value;
  229. break;
  230. case "r":
  231. target.transform.rotation = value;
  232. break;
  233. case "a":
  234. target.alpha = value;
  235. break;
  236. case "t":
  237. target.i(value); // i = setTint
  238. break;
  239. case "c":
  240. target.c.apply(target, value); // c = setColorTransform
  241. break;
  242. case "v":
  243. target.visible = value;
  244. break;
  245. case "m":
  246. target.ma(value); // ma = setMask
  247. break;
  248. }
  249. }
  250. // Assign to namespace
  251. export default Tween;