Transition.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /**
  2. * Animates the creation and destruction of child elements. This component is especially
  3. * useful for animating transitions between routes with react-router. Child elements
  4. * should be given unique keys to ensure they are properly replaced (and not merely
  5. * updated) when changes occur.
  6. *
  7. * Here is an example of how to use Transition with react-router to create a slide effect
  8. * when changing routes:
  9. *
  10. * import React from 'react';
  11. * import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
  12. * import { Transition } from '@extjs/ext-react';
  13. * import NewsFeed from './NewsFeed';
  14. * import Article from './Article';
  15. *
  16. * function Layout() {
  17. * return (
  18. * <Transition>
  19. * <Switch>
  20. * <Route path="/articles" component={NewsFeed}/>
  21. * <Route path="/articles/:id" component={Article}/>
  22. * </Switch>
  23. * </Transition>
  24. * )
  25. * }
  26. *
  27. */
  28. Ext.define('Ext.reactor.Transition', {
  29. extend: 'Ext.Container',
  30. xtype: 'transition',
  31. requires: ['Ext.fx.animation.*'],
  32. initial: true,
  33. config: {
  34. /**
  35. * @cfg {"slide"/"reveal"/"cover"/"fade"/"pop"} type
  36. * The type of animation to use.
  37. */
  38. type: 'slide',
  39. /**
  40. * @cfg {Number}
  41. * The duration of animations in milliseconds
  42. */
  43. duration: 350,
  44. /**
  45. * @cfg {String}
  46. * The easing function to use for animations. Valid values are 'ease', 'linear',
  47. * ease-in', 'ease-out', 'ease-in-out', or a cubic-bezier curve as defined by CSS.
  48. */
  49. easing: 'ease',
  50. /**
  51. * @cfg {String}
  52. * The direction of the forward animation.
  53. */
  54. direction: 'left',
  55. /**
  56. * @cfg {Boolean}
  57. * Automatically switch directions based on browser URL changes. This should
  58. * generally be set to true when animating transitions based on client-side routing.
  59. */
  60. bindDirectionToLocation: true
  61. },
  62. statics: {
  63. __reactorUpdateConfigsBeforeChildren: {
  64. location: true,
  65. direction: true
  66. }
  67. },
  68. initialize: function () {
  69. this.newLocation = location.href;
  70. this.callParent();
  71. },
  72. computeDirection: function () {
  73. var me = this,
  74. newLocation, oldLocation;
  75. if (me.getBindDirectionToLocation()) {
  76. newLocation = me.newLocation || '';
  77. oldLocation = me.oldLocation || '';
  78. if (newLocation.length > oldLocation.length && !newLocation.indexOf(oldLocation)) {
  79. return 'left';
  80. }
  81. if (newLocation.length < oldLocation.length && !oldLocation.indexOf(newLocation)) {
  82. return 'right';
  83. }
  84. }
  85. return me.getDirection();
  86. },
  87. // override add to show animation when children are added
  88. add: function (items) {
  89. var me = this,
  90. animations, i;
  91. if (!Ext.isArray(items)) {
  92. items = [items];
  93. }
  94. animations = me.createAnimations();
  95. for (i = 0; i < items.length; i++) {
  96. me.addAnimationConfigs(items[i]);
  97. }
  98. me.callParent(arguments);
  99. if (me.initial) {
  100. // don't show animation on initial render
  101. animations.showAnimation = null;
  102. me.initial = false;
  103. }
  104. items.forEach(function (item) { // need a closure for the RAF
  105. item.setStyle({ visibility: 'visible' });
  106. item.show(animations.showAnimation);
  107. // override destroy to first hide then destroy
  108. var originalDestroy = item.destroy.bind(item);
  109. item.destroy = me.destroyChild.bind(me, item, originalDestroy);
  110. });
  111. },
  112. insert: function (index, item) {
  113. // order doesn't matter since we're using a floating layout
  114. this.add(item);
  115. },
  116. destroyChild: function (item, originalDestroy) {
  117. var me = this,
  118. loc = location.href,
  119. direction, hideAnimation, type;
  120. if (item.animatingDestroy) {
  121. return;
  122. }
  123. me.oldLocation = me.newLocation || loc;
  124. me.newLocation = loc;
  125. hideAnimation = me.createAnimations().hideAnimation;
  126. type = me.getType();
  127. direction = me.computeDirection();
  128. if (type === 'cover') {
  129. item.setZIndex(direction === 'left' || direction === 'top' ? 0 : 2);
  130. } else if (type === 'reveal') {
  131. item.setZIndex(direction === 'left' || direction === 'top' ? 2 : 0);
  132. }
  133. item.animatingDestroy = true;
  134. if (item.activeAnimation) {
  135. item.activeAnimation.stop();
  136. }
  137. if (hideAnimation.type === 'reactor-delay') {
  138. Ext.defer(originalDestroy, hideAnimation.duration);
  139. } else {
  140. item.on('hide', originalDestroy);
  141. item.hide(hideAnimation);
  142. }
  143. },
  144. addAnimationConfigs: function (child) {
  145. child.setConfig({
  146. zIndex: 1,
  147. top: 0,
  148. left: 0,
  149. bottom: 0,
  150. right: 0,
  151. style: {
  152. // prevent new view from "flashing" in before animating in safari
  153. visibility: 'hidden'
  154. }
  155. });
  156. },
  157. createAnimations: function () {
  158. var me = this,
  159. type = me.getType(),
  160. duration = me.getDuration(),
  161. easing = me.getEasing(),
  162. direction = me.computeDirection();
  163. if (type === 'reveal') {
  164. if (direction === 'left' || direction === 'up') {
  165. return {
  166. showAnimation: null,
  167. hideAnimation: {
  168. type: 'slideOut',
  169. easing: easing,
  170. direction: direction,
  171. duration: duration
  172. }
  173. };
  174. }
  175. return {
  176. showAnimation: {
  177. type: 'slideIn',
  178. easing: easing,
  179. direction: direction,
  180. duration: duration
  181. },
  182. hideAnimation: {
  183. type: 'reactor-delay',
  184. duration: duration
  185. }
  186. };
  187. }
  188. if (type === "cover") {
  189. if (direction === 'left' || direction === 'up') {
  190. return {
  191. showAnimation: {
  192. type: 'slideIn',
  193. easing: easing,
  194. direction: direction,
  195. duration: duration
  196. },
  197. hideAnimation: {
  198. type: 'reactor-delay',
  199. duration: duration
  200. }
  201. };
  202. }
  203. return {
  204. showAnimation: null,
  205. hideAnimation: {
  206. type: 'slideOut',
  207. easing: easing,
  208. direction: direction,
  209. duration: duration
  210. }
  211. };
  212. }
  213. return {
  214. showAnimation: {
  215. type: type + 'In',
  216. easing: easing,
  217. direction: direction,
  218. duration: duration
  219. },
  220. hideAnimation: {
  221. type: type + 'Out',
  222. easing: easing,
  223. direction: direction,
  224. duration: duration
  225. }
  226. };
  227. }
  228. });