source: Dev/trunk/src/client/dojo/fx.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 12.2 KB
RevLine 
[483]1define([
2        "./_base/lang",
3        "./Evented",
4        "./_base/kernel",
5        "./_base/array",
6        "./aspect",
7        "./_base/fx",
8        "./dom",
9        "./dom-style",
10        "./dom-geometry",
11        "./ready",
12        "require" // for context sensitive loading of Toggler
13], function(lang, Evented, dojo, arrayUtil, aspect, baseFx, dom, domStyle, geom, ready, require){
14
15        // module:
16        //              dojo/fx
17       
18        // For back-compat, remove in 2.0.
19        if(!dojo.isAsync){
20                ready(0, function(){
21                        var requires = ["./fx/Toggler"];
22                        require(requires);      // use indirection so modules not rolled into a build
23                });
24        }
25
26        var coreFx = dojo.fx = {
27                // summary:
28                //              Effects library on top of Base animations
29        };
30
31        var _baseObj = {
32                        _fire: function(evt, args){
33                                if(this[evt]){
34                                        this[evt].apply(this, args||[]);
35                                }
36                                return this;
37                        }
38                };
39
40        var _chain = function(animations){
41                this._index = -1;
42                this._animations = animations||[];
43                this._current = this._onAnimateCtx = this._onEndCtx = null;
44
45                this.duration = 0;
46                arrayUtil.forEach(this._animations, function(a){
47                        this.duration += a.duration;
48                        if(a.delay){ this.duration += a.delay; }
49                }, this);
50        };
51        _chain.prototype = new Evented();
52        lang.extend(_chain, {
53                _onAnimate: function(){
54                        this._fire("onAnimate", arguments);
55                },
56                _onEnd: function(){
57                        this._onAnimateCtx.remove();
58                        this._onEndCtx.remove();
59                        this._onAnimateCtx = this._onEndCtx = null;
60                        if(this._index + 1 == this._animations.length){
61                                this._fire("onEnd");
62                        }else{
63                                // switch animations
64                                this._current = this._animations[++this._index];
65                                this._onAnimateCtx = aspect.after(this._current, "onAnimate", lang.hitch(this, "_onAnimate"), true);
66                                this._onEndCtx = aspect.after(this._current, "onEnd", lang.hitch(this, "_onEnd"), true);
67                                this._current.play(0, true);
68                        }
69                },
70                play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
71                        if(!this._current){ this._current = this._animations[this._index = 0]; }
72                        if(!gotoStart && this._current.status() == "playing"){ return this; }
73                        var beforeBegin = aspect.after(this._current, "beforeBegin", lang.hitch(this, function(){
74                                        this._fire("beforeBegin");
75                                }), true),
76                                onBegin = aspect.after(this._current, "onBegin", lang.hitch(this, function(arg){
77                                        this._fire("onBegin", arguments);
78                                }), true),
79                                onPlay = aspect.after(this._current, "onPlay", lang.hitch(this, function(arg){
80                                        this._fire("onPlay", arguments);
81                                        beforeBegin.remove();
82                                        onBegin.remove();
83                                        onPlay.remove();
84                                }));
85                        if(this._onAnimateCtx){
86                                this._onAnimateCtx.remove();
87                        }
88                        this._onAnimateCtx = aspect.after(this._current, "onAnimate", lang.hitch(this, "_onAnimate"), true);
89                        if(this._onEndCtx){
90                                this._onEndCtx.remove();
91                        }
92                        this._onEndCtx = aspect.after(this._current, "onEnd", lang.hitch(this, "_onEnd"), true);
93                        this._current.play.apply(this._current, arguments);
94                        return this;
95                },
96                pause: function(){
97                        if(this._current){
98                                var e = aspect.after(this._current, "onPause", lang.hitch(this, function(arg){
99                                                this._fire("onPause", arguments);
100                                                e.remove();
101                                        }), true);
102                                this._current.pause();
103                        }
104                        return this;
105                },
106                gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
107                        this.pause();
108                        var offset = this.duration * percent;
109                        this._current = null;
110                        arrayUtil.some(this._animations, function(a){
111                                if(a.duration <= offset){
112                                        this._current = a;
113                                        return true;
114                                }
115                                offset -= a.duration;
116                                return false;
117                        });
118                        if(this._current){
119                                this._current.gotoPercent(offset / this._current.duration, andPlay);
120                        }
121                        return this;
122                },
123                stop: function(/*boolean?*/ gotoEnd){
124                        if(this._current){
125                                if(gotoEnd){
126                                        for(; this._index + 1 < this._animations.length; ++this._index){
127                                                this._animations[this._index].stop(true);
128                                        }
129                                        this._current = this._animations[this._index];
130                                }
131                                var e = aspect.after(this._current, "onStop", lang.hitch(this, function(arg){
132                                                this._fire("onStop", arguments);
133                                                e.remove();
134                                        }), true);
135                                this._current.stop();
136                        }
137                        return this;
138                },
139                status: function(){
140                        return this._current ? this._current.status() : "stopped";
141                },
142                destroy: function(){
143                        if(this._onAnimateCtx){ this._onAnimateCtx.remove(); }
144                        if(this._onEndCtx){ this._onEndCtx.remove(); }
145                }
146        });
147        lang.extend(_chain, _baseObj);
148
149        coreFx.chain = function(/*dojo/_base/fx.Animation[]*/ animations){
150                // summary:
151                //              Chain a list of `dojo/_base/fx.Animation`s to run in sequence
152                //
153                // description:
154                //              Return a `dojo/_base/fx.Animation` which will play all passed
155                //              `dojo/_base/fx.Animation` instances in sequence, firing its own
156                //              synthesized events simulating a single animation. (eg:
157                //              onEnd of this animation means the end of the chain,
158                //              not the individual animations within)
159                //
160                // example:
161                //      Once `node` is faded out, fade in `otherNode`
162                //      |       require(["dojo/fx"], function(fx){
163                //      |               fx.chain([
164                //      |                       fx.fadeIn({ node:node }),
165                //      |                       fx.fadeOut({ node:otherNode })
166                //      |               ]).play();
167                //      |       });
168                //
169                return new _chain(animations); // dojo/_base/fx.Animation
170        };
171
172        var _combine = function(animations){
173                this._animations = animations||[];
174                this._connects = [];
175                this._finished = 0;
176
177                this.duration = 0;
178                arrayUtil.forEach(animations, function(a){
179                        var duration = a.duration;
180                        if(a.delay){ duration += a.delay; }
181                        if(this.duration < duration){ this.duration = duration; }
182                        this._connects.push(aspect.after(a, "onEnd", lang.hitch(this, "_onEnd"), true));
183                }, this);
184
185                this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration});
186                var self = this;
187                arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
188                        function(evt){
189                                self._connects.push(aspect.after(self._pseudoAnimation, evt,
190                                        function(){ self._fire(evt, arguments); },
191                                true));
192                        }
193                );
194        };
195        lang.extend(_combine, {
196                _doAction: function(action, args){
197                        arrayUtil.forEach(this._animations, function(a){
198                                a[action].apply(a, args);
199                        });
200                        return this;
201                },
202                _onEnd: function(){
203                        if(++this._finished > this._animations.length){
204                                this._fire("onEnd");
205                        }
206                },
207                _call: function(action, args){
208                        var t = this._pseudoAnimation;
209                        t[action].apply(t, args);
210                },
211                play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
212                        this._finished = 0;
213                        this._doAction("play", arguments);
214                        this._call("play", arguments);
215                        return this;
216                },
217                pause: function(){
218                        this._doAction("pause", arguments);
219                        this._call("pause", arguments);
220                        return this;
221                },
222                gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
223                        var ms = this.duration * percent;
224                        arrayUtil.forEach(this._animations, function(a){
225                                a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
226                        });
227                        this._call("gotoPercent", arguments);
228                        return this;
229                },
230                stop: function(/*boolean?*/ gotoEnd){
231                        this._doAction("stop", arguments);
232                        this._call("stop", arguments);
233                        return this;
234                },
235                status: function(){
236                        return this._pseudoAnimation.status();
237                },
238                destroy: function(){
239                        arrayUtil.forEach(this._connects, function(handle){
240                                handle.remove();
241                        });
242                }
243        });
244        lang.extend(_combine, _baseObj);
245
246        coreFx.combine = function(/*dojo/_base/fx.Animation[]*/ animations){
247                // summary:
248                //              Combine a list of `dojo/_base/fx.Animation`s to run in parallel
249                //
250                // description:
251                //              Combine an array of `dojo/_base/fx.Animation`s to run in parallel,
252                //              providing a new `dojo/_base/fx.Animation` instance encompasing each
253                //              animation, firing standard animation events.
254                //
255                // example:
256                //      Fade out `node` while fading in `otherNode` simultaneously
257                //      |       require(["dojo/fx"], function(fx){
258                //      |               fx.combine([
259                //      |                       fx.fadeIn({ node:node }),
260                //      |                       fx.fadeOut({ node:otherNode })
261                //      |               ]).play();
262                //      |       });
263                //
264                // example:
265                //      When the longest animation ends, execute a function:
266                //      |       require(["dojo/fx"], function(fx){
267                //      |               var anim = fx.combine([
268                //      |                       fx.fadeIn({ node: n, duration:700 }),
269                //      |                       fx.fadeOut({ node: otherNode, duration: 300 })
270                //      |               ]);
271                //      |               aspect.after(anim, "onEnd", function(){
272                //      |                       // overall animation is done.
273                //      |               }, true);
274                //      |               anim.play(); // play the animation
275                //      |       });
276                //
277                return new _combine(animations); // dojo/_base/fx.Animation
278        };
279
280        coreFx.wipeIn = function(/*Object*/ args){
281                // summary:
282                //              Expand a node to it's natural height.
283                //
284                // description:
285                //              Returns an animation that will expand the
286                //              node defined in 'args' object from it's current height to
287                //              it's natural height (with no scrollbar).
288                //              Node must have no margin/border/padding.
289                //
290                // args: Object
291                //              A hash-map of standard `dojo/_base/fx.Animation` constructor properties
292                //              (such as easing: node: duration: and so on)
293                //
294                // example:
295                //      |       require(["dojo/fx"], function(fx){
296                //      |               fx.wipeIn({
297                //      |                       node:"someId"
298                //      |               }).play()
299                //      |       });
300
301                var node = args.node = dom.byId(args.node), s = node.style, o;
302
303                var anim = baseFx.animateProperty(lang.mixin({
304                        properties: {
305                                height: {
306                                        // wrapped in functions so we wait till the last second to query (in case value has changed)
307                                        start: function(){
308                                                // start at current [computed] height, but use 1px rather than 0
309                                                // because 0 causes IE to display the whole panel
310                                                o = s.overflow;
311                                                s.overflow = "hidden";
312                                                if(s.visibility == "hidden" || s.display == "none"){
313                                                        s.height = "1px";
314                                                        s.display = "";
315                                                        s.visibility = "";
316                                                        return 1;
317                                                }else{
318                                                        var height = domStyle.get(node, "height");
319                                                        return Math.max(height, 1);
320                                                }
321                                        },
322                                        end: function(){
323                                                return node.scrollHeight;
324                                        }
325                                }
326                        }
327                }, args));
328
329                var fini = function(){
330                        s.height = "auto";
331                        s.overflow = o;
332                };
333                aspect.after(anim, "onStop", fini, true);
334                aspect.after(anim, "onEnd", fini, true);
335
336                return anim; // dojo/_base/fx.Animation
337        };
338
339        coreFx.wipeOut = function(/*Object*/ args){
340                // summary:
341                //              Shrink a node to nothing and hide it.
342                //
343                // description:
344                //              Returns an animation that will shrink node defined in "args"
345                //              from it's current height to 1px, and then hide it.
346                //
347                // args: Object
348                //              A hash-map of standard `dojo/_base/fx.Animation` constructor properties
349                //              (such as easing: node: duration: and so on)
350                //
351                // example:
352                //      |       require(["dojo/fx"], function(fx){
353                //      |               fx.wipeOut({ node:"someId" }).play()
354                //      |       });
355
356                var node = args.node = dom.byId(args.node), s = node.style, o;
357
358                var anim = baseFx.animateProperty(lang.mixin({
359                        properties: {
360                                height: {
361                                        end: 1 // 0 causes IE to display the whole panel
362                                }
363                        }
364                }, args));
365
366                aspect.after(anim, "beforeBegin", function(){
367                        o = s.overflow;
368                        s.overflow = "hidden";
369                        s.display = "";
370                }, true);
371                var fini = function(){
372                        s.overflow = o;
373                        s.height = "auto";
374                        s.display = "none";
375                };
376                aspect.after(anim, "onStop", fini, true);
377                aspect.after(anim, "onEnd", fini, true);
378
379                return anim; // dojo/_base/fx.Animation
380        };
381
382        coreFx.slideTo = function(/*Object*/ args){
383                // summary:
384                //              Slide a node to a new top/left position
385                //
386                // description:
387                //              Returns an animation that will slide "node"
388                //              defined in args Object from its current position to
389                //              the position defined by (args.left, args.top).
390                //
391                // args: Object
392                //              A hash-map of standard `dojo/_base/fx.Animation` constructor properties
393                //              (such as easing: node: duration: and so on). Special args members
394                //              are `top` and `left`, which indicate the new position to slide to.
395                //
396                // example:
397                //      |       .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
398
399                var node = args.node = dom.byId(args.node),
400                        top = null, left = null;
401
402                var init = (function(n){
403                        return function(){
404                                var cs = domStyle.getComputedStyle(n);
405                                var pos = cs.position;
406                                top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
407                                left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
408                                if(pos != 'absolute' && pos != 'relative'){
409                                        var ret = geom.position(n, true);
410                                        top = ret.y;
411                                        left = ret.x;
412                                        n.style.position="absolute";
413                                        n.style.top=top+"px";
414                                        n.style.left=left+"px";
415                                }
416                        };
417                })(node);
418                init();
419
420                var anim = baseFx.animateProperty(lang.mixin({
421                        properties: {
422                                top: args.top || 0,
423                                left: args.left || 0
424                        }
425                }, args));
426                aspect.after(anim, "beforeBegin", init, true);
427
428                return anim; // dojo/_base/fx.Animation
429        };
430
431        return coreFx;
432});
Note: See TracBrowser for help on using the repository browser.