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