source: Dev/branches/rest-dojo-ui/client/dojox/app/animation.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 11.4 KB
Line 
1define(["dojo/_base/kernel",
2        "dojo/_base/lang",
3        "dojo/_base/declare",
4        "dojo/_base/array",
5        "dojo/_base/Deferred",
6        "dojo/DeferredList",
7        "dojo/on",
8        "dojo/_base/sniff"],
9        function(dojo, lang, declare, array, deferred, deferredList, on, has){
10    //TODO create cross platform animation/transition effects
11    var transitionEndEventName = "transitionend";
12    var transitionPrefix = "t"; //by default use "t" prefix and "ransition" to make word "transition"
13    var translateMethodStart = "translate3d(";//Android 2.x does not support translateX in CSS Transition, we need to use translate3d in webkit browsers
14    var translateMethodEnd = ",0,0)";
15    if(has("webkit")){
16        transitionPrefix = "WebkitT";
17        transitionEndEventName = "webkitTransitionEnd";
18    }else if(has("mozilla")){
19        transitionPrefix = "MozT";
20        translateMethodStart = "translateX(";
21        translateMethodEnd = ")";
22    }
23   
24   
25
26    //TODO find a way to lock the animation and prevent animation conflict
27    declare("dojox.app.animation", null, {
28       
29
30        constructor: function(args){
31            //default config should be in animation object itself instead of its prototype
32            //otherwise, it might be easy for making mistake of modifying prototype
33            var defaultConfig = {
34                startState: {},
35                endState: {},
36                node: null,
37                duration: 250,
38                "in": true,
39                direction: 1,
40                autoClear: true
41            };
42           
43            lang.mixin(this, defaultConfig);
44            lang.mixin(this, args);
45           
46            //create the deferred object which will resolve after the animation is finished.
47            //We can rely on "onAfterEnd" function to notify the end of a single animation,
48            //but using a deferred object is easier to wait for multiple animations end.
49            if(!this.deferred){
50                this.deferred = new deferred();
51            }
52        },
53       
54        play: function(){
55            //play the animation using CSS3 Transition
56            dojox.app.animation.groupedPlay([this]);
57        },
58       
59        //method to apply the state of the transition
60        _applyState: function(state){
61            var style = this.node.style;
62            for(var property in state){
63                if(state.hasOwnProperty(property)){
64                    style[property] = state[property];
65                }
66            }
67        },
68       
69        //method to initialize state for transition
70        initState: function(){
71           
72            //apply the immediate style change for initial state.
73            this.node.style[transitionPrefix + "ransitionProperty"] = "none";
74            this.node.style[transitionPrefix + "ransitionDuration"] = "0ms";
75            this._applyState(this.startState);
76           
77        },
78       
79        _beforeStart: function(){
80            if (this.node.style.display === "none"){
81                this.node.style.display = "";
82            }
83            this.beforeStart();
84        },
85       
86        _beforeClear: function(){
87            this.node.style[transitionPrefix + "ransitionProperty"] = null;
88            this.node.style[transitionPrefix + "ransitionDuration"] = null;
89            if(this["in"] !== true){
90                this.node.style.display = "none";
91            }           
92            this.beforeClear();
93        },
94       
95        _onAfterEnd: function(){
96            this.deferred.resolve(this.node);
97            if(this.node.id && dojox.app.animation.playing[this.node.id]===this.deferred){
98                delete dojox.app.animation.playing[this.node.id];
99            }
100            this.onAfterEnd();
101        },
102       
103        beforeStart: function(){
104           
105        },
106       
107        beforeClear: function(){
108           
109        },
110       
111        onAfterEnd: function(){
112           
113        },
114       
115        //method to start the transition
116        start: function(){
117            this._beforeStart();
118           
119            var self = this;
120            //change the transition duration
121            self.node.style[transitionPrefix + "ransitionProperty"] = "all";
122            self.node.style[transitionPrefix + "ransitionDuration"] = self.duration + "ms";
123           
124            //connect to clear the transition state after the transition end.
125            //Since the transition is conducted asynchronously, we need to
126            //connect to transition end event to clear the state
127            on.once(self.node, transitionEndEventName, function(){
128                self.clear();
129            });
130           
131            this._applyState(this.endState);
132        },
133       
134        //method to clear state after transition
135        clear: function(){
136            this._beforeClear();
137            this._removeState(this.endState);
138            console.log(this.node.id + " clear.");
139            this._onAfterEnd();
140        },
141       
142        //create removeState method
143        _removeState: function(state){
144            var style = this.node.style;
145            for(var property in state){
146                if(state.hasOwnProperty(property)){
147                    style[property] = null;
148                }
149            }
150        }
151       
152    });
153   
154    //TODO add the lock mechanism for all of the transition effects
155    //     consider using only one object for one type of transition.
156    //TODO create the first animation, slide.
157    dojox.app.animation.slide = function(node, config){
158
159        //TODO create the return and set the startState, endState of the return
160        var ret = new dojox.app.animation(config);
161        ret.node = node;
162       
163        var startX = "0";
164        var endX = "0";
165       
166        if(ret["in"]){
167            if(ret.direction === 1){
168                startX = "100%";
169            }else{
170                startX = "-100%";
171            }
172        }else{
173            if(ret.direction === 1){
174                endX = "-100%";
175            }else{
176                endX = "100%";
177            }
178        }
179       
180       
181        ret.startState[transitionPrefix + "ransform"]=translateMethodStart+startX+translateMethodEnd;
182       
183        ret.endState[transitionPrefix + "ransform"]=translateMethodStart+endX+translateMethodEnd;
184       
185        return ret;
186    };
187       
188   
189    //fade in/out animation effects
190    dojox.app.animation.fade = function(node, config){
191       
192        var ret = new dojox.app.animation(config);
193        ret.node = node;
194       
195        var startOpacity = "0";
196        var endOpacity = "0";
197       
198        if(ret["in"]){
199            endOpacity = "1";
200        }else{
201            startOpacity = "1";
202        }
203       
204        lang.mixin(ret, {
205            startState:{
206                "opacity": startOpacity
207            },
208            endState:{
209                "opacity": endOpacity
210            }
211        });
212       
213        return ret;
214    };
215   
216  //fade in/out animation effects
217    dojox.app.animation.flip = function(node, config){
218       
219        var ret = new dojox.app.animation(config);
220        ret.node = node;
221       
222        if(ret["in"]){
223            //Need to set opacity here because Android 2.2 has bug that
224            //scale(...) in transform does not persist status
225            lang.mixin(ret,{
226                startState:{
227                    "opacity": "0"
228                },
229                endState:{
230                    "opacity": "1"
231                }
232            });
233            ret.startState[transitionPrefix + "ransform"]="scale(0,0.8) skew(0,-30deg)";
234            ret.endState[transitionPrefix + "ransform"]="scale(1,1) skew(0,0)";
235        }else{
236            lang.mixin(ret,{
237                startState:{
238                    "opacity": "1"
239                },
240                endState:{
241                    "opacity": "0"
242                }
243            });         
244            ret.startState[transitionPrefix + "ransform"]="scale(1,1) skew(0,0)";
245            ret.endState[transitionPrefix + "ransform"]="scale(0,0.8) skew(0,30deg)";
246        }
247       
248        return ret;
249    };
250   
251    var getWaitingList = function(/*Array*/ nodes){
252        var defs = [];
253        array.forEach(nodes, function(node){
254            //check whether the node is under other animation
255            if(node.id && dojox.app.animation.playing[node.id]){
256                //TODO hook on deferred object in dojox.app.animation.playing
257                defs.push(dojox.app.animation.playing[node.id]);
258            }
259           
260        });
261        return new deferredList(defs);
262    };
263   
264    dojox.app.animation.getWaitingList = getWaitingList;
265   
266    //TODO groupedPlay should ensure the UI update happens when
267    //all animations end.
268    //the group player to start multiple animations together
269    dojox.app.animation.groupedPlay = function(/*Array*/args){
270        //args should be array of dojox.app.animation
271       
272        var animNodes = array.filter(args, function(item){
273            return item.node;
274        });
275       
276        var waitingList = getWaitingList(animNodes);
277
278        //update registry with deferred objects in animations of args.
279        array.forEach(args, function(item){
280            if(item.node.id){
281                dojox.app.animation.playing[item.node.id] = item.deferred;
282            }
283        });
284       
285        //TODO wait for all deferred object in deferred list to resolve
286        dojo.when(waitingList, function(){
287            array.forEach(args, function(item){
288                //set the start state
289                item.initState();
290            });
291           
292            //Assume the fps of the animation should be higher than 30 fps and
293            //allow the browser to use one frame's time to redraw so that
294            //the transition can be started
295            setTimeout(function(){
296                array.forEach(args, function(item){
297                    item.start();
298                });           
299            }, 33);
300        });       
301    };
302   
303    //the chain player to start multiple animations one by one
304    dojox.app.animation.chainedPlay = function(/*Array*/args){
305        //args should be array of dojox.app.animation
306       
307        var animNodes = array.filter(args, function(item){
308            return item.node;
309        });
310       
311        var waitingList = getWaitingList(animNodes);
312
313        //update registry with deferred objects in animations of args.
314        array.forEach(args, function(item){
315            if(item.node.id){
316                dojox.app.animation.playing[item.node.id] = item.deferred;
317            }
318        });
319       
320        dojo.when(waitingList, function(){
321            array.forEach(args, function(item){
322                //set the start state
323                item.initState();
324            });
325           
326            //chain animations together
327            for (var i=1, len=args.length; i < len; i++){
328                args[i-1].deferred.then(lang.hitch(args[i], function(){
329                    this.start();
330                }));
331            }
332           
333            //Assume the fps of the animation should be higher than 30 fps and
334            //allow the browser to use one frame's time to redraw so that
335            //the transition can be started
336            setTimeout(function(){
337                args[0].start();
338            }, 33);
339        });       
340    };
341   
342    //TODO complete the registry mechanism for animation handling and prevent animation conflicts
343    dojox.app.animation.playing = {};
344   
345    return dojox.app.animation;
346});
Note: See TracBrowser for help on using the repository browser.