summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/app/animation.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/app/animation.js')
-rw-r--r--js/dojo/dojox/app/animation.js347
1 files changed, 347 insertions, 0 deletions
diff --git a/js/dojo/dojox/app/animation.js b/js/dojo/dojox/app/animation.js
new file mode 100644
index 0000000..f1d90b0
--- /dev/null
+++ b/js/dojo/dojox/app/animation.js
@@ -0,0 +1,347 @@
+//>>built
+define("dojox/app/animation", ["dojo/_base/kernel",
+ "dojo/_base/lang",
+ "dojo/_base/declare",
+ "dojo/_base/array",
+ "dojo/_base/Deferred",
+ "dojo/DeferredList",
+ "dojo/on",
+ "dojo/_base/sniff"],
+ function(dojo, lang, declare, array, deferred, deferredList, on, has){
+ //TODO create cross platform animation/transition effects
+ var transitionEndEventName = "transitionend";
+ var transitionPrefix = "t"; //by default use "t" prefix and "ransition" to make word "transition"
+ var translateMethodStart = "translate3d(";//Android 2.x does not support translateX in CSS Transition, we need to use translate3d in webkit browsers
+ var translateMethodEnd = ",0,0)";
+ if(has("webkit")){
+ transitionPrefix = "WebkitT";
+ transitionEndEventName = "webkitTransitionEnd";
+ }else if(has("mozilla")){
+ transitionPrefix = "MozT";
+ translateMethodStart = "translateX(";
+ translateMethodEnd = ")";
+ }
+
+
+
+ //TODO find a way to lock the animation and prevent animation conflict
+ declare("dojox.app.animation", null, {
+
+
+ constructor: function(args){
+ //default config should be in animation object itself instead of its prototype
+ //otherwise, it might be easy for making mistake of modifying prototype
+ var defaultConfig = {
+ startState: {},
+ endState: {},
+ node: null,
+ duration: 250,
+ "in": true,
+ direction: 1,
+ autoClear: true
+ };
+
+ lang.mixin(this, defaultConfig);
+ lang.mixin(this, args);
+
+ //create the deferred object which will resolve after the animation is finished.
+ //We can rely on "onAfterEnd" function to notify the end of a single animation,
+ //but using a deferred object is easier to wait for multiple animations end.
+ if(!this.deferred){
+ this.deferred = new deferred();
+ }
+ },
+
+ play: function(){
+ //play the animation using CSS3 Transition
+ dojox.app.animation.groupedPlay([this]);
+ },
+
+ //method to apply the state of the transition
+ _applyState: function(state){
+ var style = this.node.style;
+ for(var property in state){
+ if(state.hasOwnProperty(property)){
+ style[property] = state[property];
+ }
+ }
+ },
+
+ //method to initialize state for transition
+ initState: function(){
+
+ //apply the immediate style change for initial state.
+ this.node.style[transitionPrefix + "ransitionProperty"] = "none";
+ this.node.style[transitionPrefix + "ransitionDuration"] = "0ms";
+ this._applyState(this.startState);
+
+ },
+
+ _beforeStart: function(){
+ if (this.node.style.display === "none"){
+ this.node.style.display = "";
+ }
+ this.beforeStart();
+ },
+
+ _beforeClear: function(){
+ this.node.style[transitionPrefix + "ransitionProperty"] = null;
+ this.node.style[transitionPrefix + "ransitionDuration"] = null;
+ if(this["in"] !== true){
+ this.node.style.display = "none";
+ }
+ this.beforeClear();
+ },
+
+ _onAfterEnd: function(){
+ this.deferred.resolve(this.node);
+ if(this.node.id && dojox.app.animation.playing[this.node.id]===this.deferred){
+ delete dojox.app.animation.playing[this.node.id];
+ }
+ this.onAfterEnd();
+ },
+
+ beforeStart: function(){
+
+ },
+
+ beforeClear: function(){
+
+ },
+
+ onAfterEnd: function(){
+
+ },
+
+ //method to start the transition
+ start: function(){
+ this._beforeStart();
+
+ var self = this;
+ //change the transition duration
+ self.node.style[transitionPrefix + "ransitionProperty"] = "all";
+ self.node.style[transitionPrefix + "ransitionDuration"] = self.duration + "ms";
+
+ //connect to clear the transition state after the transition end.
+ //Since the transition is conducted asynchronously, we need to
+ //connect to transition end event to clear the state
+ on.once(self.node, transitionEndEventName, function(){
+ self.clear();
+ });
+
+ this._applyState(this.endState);
+ },
+
+ //method to clear state after transition
+ clear: function(){
+ this._beforeClear();
+ this._removeState(this.endState);
+ console.log(this.node.id + " clear.");
+ this._onAfterEnd();
+ },
+
+ //create removeState method
+ _removeState: function(state){
+ var style = this.node.style;
+ for(var property in state){
+ if(state.hasOwnProperty(property)){
+ style[property] = null;
+ }
+ }
+ }
+
+ });
+
+ //TODO add the lock mechanism for all of the transition effects
+ // consider using only one object for one type of transition.
+ //TODO create the first animation, slide.
+ dojox.app.animation.slide = function(node, config){
+
+ //TODO create the return and set the startState, endState of the return
+ var ret = new dojox.app.animation(config);
+ ret.node = node;
+
+ var startX = "0";
+ var endX = "0";
+
+ if(ret["in"]){
+ if(ret.direction === 1){
+ startX = "100%";
+ }else{
+ startX = "-100%";
+ }
+ }else{
+ if(ret.direction === 1){
+ endX = "-100%";
+ }else{
+ endX = "100%";
+ }
+ }
+
+
+ ret.startState[transitionPrefix + "ransform"]=translateMethodStart+startX+translateMethodEnd;
+
+ ret.endState[transitionPrefix + "ransform"]=translateMethodStart+endX+translateMethodEnd;
+
+ return ret;
+ };
+
+
+ //fade in/out animation effects
+ dojox.app.animation.fade = function(node, config){
+
+ var ret = new dojox.app.animation(config);
+ ret.node = node;
+
+ var startOpacity = "0";
+ var endOpacity = "0";
+
+ if(ret["in"]){
+ endOpacity = "1";
+ }else{
+ startOpacity = "1";
+ }
+
+ lang.mixin(ret, {
+ startState:{
+ "opacity": startOpacity
+ },
+ endState:{
+ "opacity": endOpacity
+ }
+ });
+
+ return ret;
+ };
+
+ //fade in/out animation effects
+ dojox.app.animation.flip = function(node, config){
+
+ var ret = new dojox.app.animation(config);
+ ret.node = node;
+
+ if(ret["in"]){
+ //Need to set opacity here because Android 2.2 has bug that
+ //scale(...) in transform does not persist status
+ lang.mixin(ret,{
+ startState:{
+ "opacity": "0"
+ },
+ endState:{
+ "opacity": "1"
+ }
+ });
+ ret.startState[transitionPrefix + "ransform"]="scale(0,0.8) skew(0,-30deg)";
+ ret.endState[transitionPrefix + "ransform"]="scale(1,1) skew(0,0)";
+ }else{
+ lang.mixin(ret,{
+ startState:{
+ "opacity": "1"
+ },
+ endState:{
+ "opacity": "0"
+ }
+ });
+ ret.startState[transitionPrefix + "ransform"]="scale(1,1) skew(0,0)";
+ ret.endState[transitionPrefix + "ransform"]="scale(0,0.8) skew(0,30deg)";
+ }
+
+ return ret;
+ };
+
+ var getWaitingList = function(/*Array*/ nodes){
+ var defs = [];
+ array.forEach(nodes, function(node){
+ //check whether the node is under other animation
+ if(node.id && dojox.app.animation.playing[node.id]){
+ //TODO hook on deferred object in dojox.app.animation.playing
+ defs.push(dojox.app.animation.playing[node.id]);
+ }
+
+ });
+ return new deferredList(defs);
+ };
+
+ dojox.app.animation.getWaitingList = getWaitingList;
+
+ //TODO groupedPlay should ensure the UI update happens when
+ //all animations end.
+ //the group player to start multiple animations together
+ dojox.app.animation.groupedPlay = function(/*Array*/args){
+ //args should be array of dojox.app.animation
+
+ var animNodes = array.filter(args, function(item){
+ return item.node;
+ });
+
+ var waitingList = getWaitingList(animNodes);
+
+ //update registry with deferred objects in animations of args.
+ array.forEach(args, function(item){
+ if(item.node.id){
+ dojox.app.animation.playing[item.node.id] = item.deferred;
+ }
+ });
+
+ //TODO wait for all deferred object in deferred list to resolve
+ dojo.when(waitingList, function(){
+ array.forEach(args, function(item){
+ //set the start state
+ item.initState();
+ });
+
+ //Assume the fps of the animation should be higher than 30 fps and
+ //allow the browser to use one frame's time to redraw so that
+ //the transition can be started
+ setTimeout(function(){
+ array.forEach(args, function(item){
+ item.start();
+ });
+ }, 33);
+ });
+ };
+
+ //the chain player to start multiple animations one by one
+ dojox.app.animation.chainedPlay = function(/*Array*/args){
+ //args should be array of dojox.app.animation
+
+ var animNodes = array.filter(args, function(item){
+ return item.node;
+ });
+
+ var waitingList = getWaitingList(animNodes);
+
+ //update registry with deferred objects in animations of args.
+ array.forEach(args, function(item){
+ if(item.node.id){
+ dojox.app.animation.playing[item.node.id] = item.deferred;
+ }
+ });
+
+ dojo.when(waitingList, function(){
+ array.forEach(args, function(item){
+ //set the start state
+ item.initState();
+ });
+
+ //chain animations together
+ for (var i=1, len=args.length; i < len; i++){
+ args[i-1].deferred.then(lang.hitch(args[i], function(){
+ this.start();
+ }));
+ }
+
+ //Assume the fps of the animation should be higher than 30 fps and
+ //allow the browser to use one frame's time to redraw so that
+ //the transition can be started
+ setTimeout(function(){
+ args[0].start();
+ }, 33);
+ });
+ };
+
+ //TODO complete the registry mechanism for animation handling and prevent animation conflicts
+ dojox.app.animation.playing = {};
+
+ return dojox.app.animation;
+});