API Docs for: 3.10.3
Show:

File: app/js/app-extensions/app-transitions-native.js

  1. /**
  2. Provides the implementation of view transitions for `Y.App.Transitions` in
  3. browsers which support native CSS3 transitions.
  4.  
  5. @module app
  6. @submodule app-transitions-native
  7. @since 3.5.0
  8. **/
  9.  
  10. var AppTransitions = Y.App.Transitions;
  11.  
  12. /**
  13. Provides the implementation of view transitions for `Y.App.Transitions` in
  14. browsers which support native CSS3 transitions.
  15.  
  16. When this module is used, `Y.App.TransitionsNative` will automatically mix
  17. itself in to `Y.App`.
  18.  
  19. @class App.TransitionsNative
  20. @extensionfor App
  21. @since 3.5.0
  22. **/
  23. function AppTransitionsNative() {}
  24.  
  25. AppTransitionsNative.prototype = {
  26. // -- Protected Properties -------------------------------------------------
  27.  
  28. /**
  29. Whether this app is currently transitioning its `activeView`.
  30.  
  31. @property _transitioning
  32. @type Boolean
  33. @default false
  34. @protected
  35. @since 3.5.0
  36. **/
  37.  
  38. /**
  39. A queue that holds pending calls to this app's `_uiTransitionActiveView()`
  40. method.
  41.  
  42. @property _viewTransitionQueue
  43. @type Array
  44. @default []
  45. @protected
  46. @since 3.5.0
  47. **/
  48.  
  49. // -- Lifecycle Methods ----------------------------------------------------
  50.  
  51. initializer: function () {
  52. this._transitioning = false;
  53. this._viewTransitionQueue = [];
  54.  
  55. // TODO: Consider the AOP approach that `Plugin.WidgetAnim` uses.
  56. Y.Do.before(this._queueActiveView, this, '_uiSetActiveView');
  57. },
  58.  
  59. // -- Protected Methods ----------------------------------------------------
  60.  
  61. /**
  62. Dequeues any pending calls to `_uiTransitionActiveView()`.
  63.  
  64. **Note:** When there is more than one queued transition, only the most
  65. recent `activeView` change will be visually transitioned, while the others
  66. will have their `transition` option overridden to `false`.
  67.  
  68. @method _dequeueActiveView
  69. @protected
  70. @since 3.5.0
  71. **/
  72. _dequeueActiveView: function () {
  73. var queue = this._viewTransitionQueue,
  74. transition = queue.shift(),
  75. options;
  76.  
  77. if (transition) {
  78. // When items are still left in the queue, override the transition
  79. // so it does not run.
  80. if (queue.length) {
  81. // Overrides `transition` option and splices in the new options.
  82. options = Y.merge(transition[2], {transition: false});
  83. transition.splice(2, 1, options);
  84. }
  85.  
  86. this._uiTransitionActiveView.apply(this, transition);
  87. }
  88. },
  89.  
  90. /**
  91. Returns an object containing a named fx for both `viewIn` and `viewOut`
  92. based on the relationship between the specified `newView` and `oldView`.
  93.  
  94. @method _getFx
  95. @param {View} newView The view being transitioned-in.
  96. @param {View} oldView The view being transitioned-out.
  97. @param {String} [transition] The preferred transition to use.
  98. @return {Object} An object containing a named fx for both `viewIn` and
  99. `viewOut`.
  100. @protected
  101. @since 3.5.0
  102. **/
  103. _getFx: function (newView, oldView, transition) {
  104. var fx = AppTransitions.FX,
  105. transitions = this.get('transitions');
  106.  
  107. if (transition === false || !transitions) {
  108. return null;
  109. }
  110.  
  111. if (transition) {
  112. return fx[transition];
  113. }
  114.  
  115. if (this._isChildView(newView, oldView)) {
  116. return fx[transitions.toChild];
  117. }
  118.  
  119. if (this._isParentView(newView, oldView)) {
  120. return fx[transitions.toParent];
  121. }
  122.  
  123. return fx[transitions.navigate];
  124. },
  125.  
  126. /**
  127. Queues calls to `_uiTransitionActiveView()` to make sure a currently running
  128. transition isn't interrupted.
  129.  
  130. **Note:** This method prevents the default `_uiSetActiveView()` method from
  131. running.
  132.  
  133. @method _queueActiveView
  134. @protected
  135. @since 3.5.0
  136. **/
  137. _queueActiveView: function () {
  138. var args = Y.Array(arguments, 0, true);
  139.  
  140. this._viewTransitionQueue.push(args);
  141.  
  142. if (!this._transitioning) {
  143. this._dequeueActiveView();
  144. }
  145.  
  146. return new Y.Do.Prevent();
  147. },
  148.  
  149. /**
  150. Performs the actual change of this app's `activeView` by visually
  151. transitioning between the `newView` and `oldView` using any specified
  152. `options`.
  153.  
  154. The `newView` is attached to the app by rendering it to the `viewContainer`,
  155. and making this app a bubble target of its events.
  156.  
  157. The `oldView` is detached from the app by removing it from the
  158. `viewContainer`, and removing this app as a bubble target for its events.
  159. The `oldView` will either be preserved or properly destroyed.
  160.  
  161. **Note:** This method overrides `_uiSetActiveView()` and provides all of its
  162. functionality plus supports visual transitions. Also, the `activeView`
  163. attribute is read-only and can be changed by calling the `showView()`
  164. method.
  165.  
  166. @method _uiTransitionActiveView
  167. @param {View} newView The View which is now this app's `activeView`.
  168. @param {View} [oldView] The View which was this app's `activeView`.
  169. @param {Object} [options] Optional object containing any of the following
  170. properties:
  171. @param {Function} [options.callback] Optional callback function to call
  172. after new `activeView` is ready to use, the function will be passed:
  173. @param {View} options.callback.view A reference to the new
  174. `activeView`.
  175. @param {Boolean} [options.prepend=false] Whether the `view` should be
  176. prepended instead of appended to the `viewContainer`.
  177. @param {Boolean} [options.render] Whether the `view` should be rendered.
  178. **Note:** If no value is specified, a view instance will only be
  179. rendered if it's newly created by this method.
  180. @param {Boolean|String} [options.transition] Optional transition override.
  181. A transition can be specified which will override the default, or
  182. `false` for no transition.
  183. @param {Boolean} [options.update=false] Whether an existing view should
  184. have its attributes updated by passing the `config` object to its
  185. `setAttrs()` method. **Note:** This option does not have an effect if
  186. the `view` instance is created as a result of calling this method.
  187. @protected
  188. @since 3.5.0
  189. **/
  190. _uiTransitionActiveView: function (newView, oldView, options) {
  191. options || (options = {});
  192.  
  193. var callback = options.callback,
  194. container, transitioning, isChild, isParent, prepend,
  195. fx, fxConfig, transitions;
  196.  
  197. // Quits early when to new and old views are the same.
  198. if (newView === oldView) {
  199. callback && callback.call(this, newView);
  200.  
  201. this._transitioning = false;
  202. return this._dequeueActiveView();
  203. }
  204.  
  205. fx = this._getFx(newView, oldView, options.transition);
  206. isChild = this._isChildView(newView, oldView);
  207. isParent = !isChild && this._isParentView(newView, oldView);
  208. prepend = !!options.prepend || isParent;
  209.  
  210. // Preforms simply attach/detach of the new and old view respectively
  211. // when there's no transition to perform.
  212. if (!fx) {
  213. this._attachView(newView, prepend);
  214. this._detachView(oldView);
  215. callback && callback.call(this, newView);
  216.  
  217. this._transitioning = false;
  218. return this._dequeueActiveView();
  219. }
  220.  
  221. this._transitioning = true;
  222.  
  223. container = this.get('container');
  224. transitioning = Y.App.CLASS_NAMES.transitioning;
  225.  
  226. container.addClass(transitioning);
  227.  
  228. this._attachView(newView, prepend);
  229.  
  230. // Called when view transitions completed, if none were added this will
  231. // run right away.
  232. function complete() {
  233. this._detachView(oldView);
  234. container.removeClass(transitioning);
  235. callback && callback.call(this, newView);
  236.  
  237. this._transitioning = false;
  238. return this._dequeueActiveView();
  239. }
  240.  
  241. // Setup a new stack to run the view transitions in parallel.
  242. transitions = new Y.Parallel({context: this});
  243. fxConfig = {
  244. crossView: !!oldView && !!newView,
  245. prepended: prepend
  246. };
  247.  
  248. // Transition the new view first to prevent a gap when sliding.
  249. if (newView && fx.viewIn) {
  250. newView.get('container')
  251. .transition(fx.viewIn, fxConfig, transitions.add());
  252. }
  253.  
  254. if (oldView && fx.viewOut) {
  255. oldView.get('container')
  256. .transition(fx.viewOut, fxConfig, transitions.add());
  257. }
  258.  
  259. transitions.done(complete);
  260. }
  261. };
  262.  
  263. // -- Transition fx ------------------------------------------------------------
  264. Y.mix(Y.Transition.fx, {
  265. 'app:fadeIn': {
  266. opacity : 1,
  267. duration: 0.3,
  268.  
  269. on: {
  270. start: function (data) {
  271. var styles = {opacity: 0},
  272. config = data.config;
  273.  
  274. if (config.crossView && !config.prepended) {
  275. styles.transform = 'translateX(-100%)';
  276. }
  277.  
  278. this.setStyles(styles);
  279. },
  280.  
  281. end: function () {
  282. this.setStyle('transform', 'translateX(0)');
  283. }
  284. }
  285. },
  286.  
  287. 'app:fadeOut': {
  288. opacity : 0,
  289. duration: 0.3,
  290.  
  291. on: {
  292. start: function (data) {
  293. var styles = {opacity: 1},
  294. config = data.config;
  295.  
  296. if (config.crossView && config.prepended) {
  297. styles.transform = 'translateX(-100%)';
  298. }
  299.  
  300. this.setStyles(styles);
  301. },
  302.  
  303. end: function () {
  304. this.setStyle('transform', 'translateX(0)');
  305. }
  306. }
  307. },
  308.  
  309. 'app:slideLeft': {
  310. duration : 0.3,
  311. transform: 'translateX(-100%)',
  312.  
  313. on: {
  314. start: function () {
  315. this.setStyles({
  316. opacity : 1,
  317. transform: 'translateX(0%)'
  318. });
  319. },
  320.  
  321. end: function () {
  322. this.setStyle('transform', 'translateX(0)');
  323. }
  324. }
  325. },
  326.  
  327. 'app:slideRight': {
  328. duration : 0.3,
  329. transform: 'translateX(0)',
  330.  
  331. on: {
  332. start: function () {
  333. this.setStyles({
  334. opacity : 1,
  335. transform: 'translateX(-100%)'
  336. });
  337. },
  338.  
  339. end: function () {
  340. this.setStyle('transform', 'translateX(0)');
  341. }
  342. }
  343. }
  344. });
  345.  
  346. // -- Namespacae ---------------------------------------------------------------
  347. Y.App.TransitionsNative = AppTransitionsNative;
  348. Y.Base.mix(Y.App, [AppTransitionsNative]);
  349.