[483] | 1 | define(["./kernel", "./config", /*===== "./declare", =====*/ "./lang", "../Evented", "./Color", "../aspect", "../sniff", "../dom", "../dom-style"], |
---|
| 2 | function(dojo, config, /*===== declare, =====*/ lang, Evented, Color, aspect, has, dom, style){ |
---|
| 3 | // module: |
---|
| 4 | // dojo/_base/fx |
---|
| 5 | // notes: |
---|
| 6 | // Animation loosely package based on Dan Pupius' work, contributed under CLA; see |
---|
| 7 | // http://pupius.co.uk/js/Toolkit.Drawing.js |
---|
| 8 | |
---|
| 9 | var _mixin = lang.mixin; |
---|
| 10 | |
---|
| 11 | // Module export |
---|
| 12 | var basefx = { |
---|
| 13 | // summary: |
---|
| 14 | // This module defines the base dojo/_base/fx implementation. |
---|
| 15 | }; |
---|
| 16 | |
---|
| 17 | var _Line = basefx._Line = function(/*int*/ start, /*int*/ end){ |
---|
| 18 | // summary: |
---|
| 19 | // Object used to generate values from a start value to an end value |
---|
| 20 | // start: int |
---|
| 21 | // Beginning value for range |
---|
| 22 | // end: int |
---|
| 23 | // Ending value for range |
---|
| 24 | this.start = start; |
---|
| 25 | this.end = end; |
---|
| 26 | }; |
---|
| 27 | |
---|
| 28 | _Line.prototype.getValue = function(/*float*/ n){ |
---|
| 29 | // summary: |
---|
| 30 | // Returns the point on the line |
---|
| 31 | // n: |
---|
| 32 | // a floating point number greater than 0 and less than 1 |
---|
| 33 | return ((this.end - this.start) * n) + this.start; // Decimal |
---|
| 34 | }; |
---|
| 35 | |
---|
| 36 | var Animation = basefx.Animation = function(args){ |
---|
| 37 | // summary: |
---|
| 38 | // A generic animation class that fires callbacks into its handlers |
---|
| 39 | // object at various states. |
---|
| 40 | // description: |
---|
| 41 | // A generic animation class that fires callbacks into its handlers |
---|
| 42 | // object at various states. Nearly all dojo animation functions |
---|
| 43 | // return an instance of this method, usually without calling the |
---|
| 44 | // .play() method beforehand. Therefore, you will likely need to |
---|
| 45 | // call .play() on instances of `Animation` when one is |
---|
| 46 | // returned. |
---|
| 47 | // args: Object |
---|
| 48 | // The 'magic argument', mixing all the properties into this |
---|
| 49 | // animation instance. |
---|
| 50 | |
---|
| 51 | _mixin(this, args); |
---|
| 52 | if(lang.isArray(this.curve)){ |
---|
| 53 | this.curve = new _Line(this.curve[0], this.curve[1]); |
---|
| 54 | } |
---|
| 55 | |
---|
| 56 | }; |
---|
| 57 | Animation.prototype = new Evented(); |
---|
| 58 | |
---|
| 59 | lang.extend(Animation, { |
---|
| 60 | // duration: Integer |
---|
| 61 | // The time in milliseconds the animation will take to run |
---|
| 62 | duration: 350, |
---|
| 63 | |
---|
| 64 | /*===== |
---|
| 65 | // curve: _Line|Array |
---|
| 66 | // A two element array of start and end values, or a `_Line` instance to be |
---|
| 67 | // used in the Animation. |
---|
| 68 | curve: null, |
---|
| 69 | |
---|
| 70 | // easing: Function? |
---|
| 71 | // A Function to adjust the acceleration (or deceleration) of the progress |
---|
| 72 | // across a _Line |
---|
| 73 | easing: null, |
---|
| 74 | =====*/ |
---|
| 75 | |
---|
| 76 | // repeat: Integer? |
---|
| 77 | // The number of times to loop the animation |
---|
| 78 | repeat: 0, |
---|
| 79 | |
---|
| 80 | // rate: Integer? |
---|
| 81 | // the time in milliseconds to wait before advancing to next frame |
---|
| 82 | // (used as a fps timer: 1000/rate = fps) |
---|
| 83 | rate: 20 /* 50 fps */, |
---|
| 84 | |
---|
| 85 | /*===== |
---|
| 86 | // delay: Integer? |
---|
| 87 | // The time in milliseconds to wait before starting animation after it |
---|
| 88 | // has been .play()'ed |
---|
| 89 | delay: null, |
---|
| 90 | |
---|
| 91 | // beforeBegin: Event? |
---|
| 92 | // Synthetic event fired before a Animation begins playing (synchronous) |
---|
| 93 | beforeBegin: null, |
---|
| 94 | |
---|
| 95 | // onBegin: Event? |
---|
| 96 | // Synthetic event fired as a Animation begins playing (useful?) |
---|
| 97 | onBegin: null, |
---|
| 98 | |
---|
| 99 | // onAnimate: Event? |
---|
| 100 | // Synthetic event fired at each interval of the Animation |
---|
| 101 | onAnimate: null, |
---|
| 102 | |
---|
| 103 | // onEnd: Event? |
---|
| 104 | // Synthetic event fired after the final frame of the Animation |
---|
| 105 | onEnd: null, |
---|
| 106 | |
---|
| 107 | // onPlay: Event? |
---|
| 108 | // Synthetic event fired any time the Animation is play()'ed |
---|
| 109 | onPlay: null, |
---|
| 110 | |
---|
| 111 | // onPause: Event? |
---|
| 112 | // Synthetic event fired when the Animation is paused |
---|
| 113 | onPause: null, |
---|
| 114 | |
---|
| 115 | // onStop: Event |
---|
| 116 | // Synthetic event fires when the Animation is stopped |
---|
| 117 | onStop: null, |
---|
| 118 | |
---|
| 119 | =====*/ |
---|
| 120 | |
---|
| 121 | _percent: 0, |
---|
| 122 | _startRepeatCount: 0, |
---|
| 123 | |
---|
| 124 | _getStep: function(){ |
---|
| 125 | var _p = this._percent, |
---|
| 126 | _e = this.easing |
---|
| 127 | ; |
---|
| 128 | return _e ? _e(_p) : _p; |
---|
| 129 | }, |
---|
| 130 | _fire: function(/*Event*/ evt, /*Array?*/ args){ |
---|
| 131 | // summary: |
---|
| 132 | // Convenience function. Fire event "evt" and pass it the |
---|
| 133 | // arguments specified in "args". |
---|
| 134 | // description: |
---|
| 135 | // Convenience function. Fire event "evt" and pass it the |
---|
| 136 | // arguments specified in "args". |
---|
| 137 | // Fires the callback in the scope of this Animation |
---|
| 138 | // instance. |
---|
| 139 | // evt: |
---|
| 140 | // The event to fire. |
---|
| 141 | // args: |
---|
| 142 | // The arguments to pass to the event. |
---|
| 143 | var a = args||[]; |
---|
| 144 | if(this[evt]){ |
---|
| 145 | if(config.debugAtAllCosts){ |
---|
| 146 | this[evt].apply(this, a); |
---|
| 147 | }else{ |
---|
| 148 | try{ |
---|
| 149 | this[evt].apply(this, a); |
---|
| 150 | }catch(e){ |
---|
| 151 | // squelch and log because we shouldn't allow exceptions in |
---|
| 152 | // synthetic event handlers to cause the internal timer to run |
---|
| 153 | // amuck, potentially pegging the CPU. I'm not a fan of this |
---|
| 154 | // squelch, but hopefully logging will make it clear what's |
---|
| 155 | // going on |
---|
| 156 | console.error("exception in animation handler for:", evt); |
---|
| 157 | console.error(e); |
---|
| 158 | } |
---|
| 159 | } |
---|
| 160 | } |
---|
| 161 | return this; // Animation |
---|
| 162 | }, |
---|
| 163 | |
---|
| 164 | play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ |
---|
| 165 | // summary: |
---|
| 166 | // Start the animation. |
---|
| 167 | // delay: |
---|
| 168 | // How many milliseconds to delay before starting. |
---|
| 169 | // gotoStart: |
---|
| 170 | // If true, starts the animation from the beginning; otherwise, |
---|
| 171 | // starts it from its current position. |
---|
| 172 | // returns: Animation |
---|
| 173 | // The instance to allow chaining. |
---|
| 174 | |
---|
| 175 | var _t = this; |
---|
| 176 | if(_t._delayTimer){ _t._clearTimer(); } |
---|
| 177 | if(gotoStart){ |
---|
| 178 | _t._stopTimer(); |
---|
| 179 | _t._active = _t._paused = false; |
---|
| 180 | _t._percent = 0; |
---|
| 181 | }else if(_t._active && !_t._paused){ |
---|
| 182 | return _t; |
---|
| 183 | } |
---|
| 184 | |
---|
| 185 | _t._fire("beforeBegin", [_t.node]); |
---|
| 186 | |
---|
| 187 | var de = delay || _t.delay, |
---|
| 188 | _p = lang.hitch(_t, "_play", gotoStart); |
---|
| 189 | |
---|
| 190 | if(de > 0){ |
---|
| 191 | _t._delayTimer = setTimeout(_p, de); |
---|
| 192 | return _t; |
---|
| 193 | } |
---|
| 194 | _p(); |
---|
| 195 | return _t; // Animation |
---|
| 196 | }, |
---|
| 197 | |
---|
| 198 | _play: function(gotoStart){ |
---|
| 199 | var _t = this; |
---|
| 200 | if(_t._delayTimer){ _t._clearTimer(); } |
---|
| 201 | _t._startTime = new Date().valueOf(); |
---|
| 202 | if(_t._paused){ |
---|
| 203 | _t._startTime -= _t.duration * _t._percent; |
---|
| 204 | } |
---|
| 205 | |
---|
| 206 | _t._active = true; |
---|
| 207 | _t._paused = false; |
---|
| 208 | var value = _t.curve.getValue(_t._getStep()); |
---|
| 209 | if(!_t._percent){ |
---|
| 210 | if(!_t._startRepeatCount){ |
---|
| 211 | _t._startRepeatCount = _t.repeat; |
---|
| 212 | } |
---|
| 213 | _t._fire("onBegin", [value]); |
---|
| 214 | } |
---|
| 215 | |
---|
| 216 | _t._fire("onPlay", [value]); |
---|
| 217 | |
---|
| 218 | _t._cycle(); |
---|
| 219 | return _t; // Animation |
---|
| 220 | }, |
---|
| 221 | |
---|
| 222 | pause: function(){ |
---|
| 223 | // summary: |
---|
| 224 | // Pauses a running animation. |
---|
| 225 | var _t = this; |
---|
| 226 | if(_t._delayTimer){ _t._clearTimer(); } |
---|
| 227 | _t._stopTimer(); |
---|
| 228 | if(!_t._active){ return _t; /*Animation*/ } |
---|
| 229 | _t._paused = true; |
---|
| 230 | _t._fire("onPause", [_t.curve.getValue(_t._getStep())]); |
---|
| 231 | return _t; // Animation |
---|
| 232 | }, |
---|
| 233 | |
---|
| 234 | gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){ |
---|
| 235 | // summary: |
---|
| 236 | // Sets the progress of the animation. |
---|
| 237 | // percent: |
---|
| 238 | // A percentage in decimal notation (between and including 0.0 and 1.0). |
---|
| 239 | // andPlay: |
---|
| 240 | // If true, play the animation after setting the progress. |
---|
| 241 | var _t = this; |
---|
| 242 | _t._stopTimer(); |
---|
| 243 | _t._active = _t._paused = true; |
---|
| 244 | _t._percent = percent; |
---|
| 245 | if(andPlay){ _t.play(); } |
---|
| 246 | return _t; // Animation |
---|
| 247 | }, |
---|
| 248 | |
---|
| 249 | stop: function(/*boolean?*/ gotoEnd){ |
---|
| 250 | // summary: |
---|
| 251 | // Stops a running animation. |
---|
| 252 | // gotoEnd: |
---|
| 253 | // If true, the animation will end. |
---|
| 254 | var _t = this; |
---|
| 255 | if(_t._delayTimer){ _t._clearTimer(); } |
---|
| 256 | if(!_t._timer){ return _t; /* Animation */ } |
---|
| 257 | _t._stopTimer(); |
---|
| 258 | if(gotoEnd){ |
---|
| 259 | _t._percent = 1; |
---|
| 260 | } |
---|
| 261 | _t._fire("onStop", [_t.curve.getValue(_t._getStep())]); |
---|
| 262 | _t._active = _t._paused = false; |
---|
| 263 | return _t; // Animation |
---|
| 264 | }, |
---|
| 265 | |
---|
| 266 | status: function(){ |
---|
| 267 | // summary: |
---|
| 268 | // Returns a string token representation of the status of |
---|
| 269 | // the animation, one of: "paused", "playing", "stopped" |
---|
| 270 | if(this._active){ |
---|
| 271 | return this._paused ? "paused" : "playing"; // String |
---|
| 272 | } |
---|
| 273 | return "stopped"; // String |
---|
| 274 | }, |
---|
| 275 | |
---|
| 276 | _cycle: function(){ |
---|
| 277 | var _t = this; |
---|
| 278 | if(_t._active){ |
---|
| 279 | var curr = new Date().valueOf(); |
---|
| 280 | // Allow durations of 0 (instant) by setting step to 1 - see #13798 |
---|
| 281 | var step = _t.duration === 0 ? 1 : (curr - _t._startTime) / (_t.duration); |
---|
| 282 | |
---|
| 283 | if(step >= 1){ |
---|
| 284 | step = 1; |
---|
| 285 | } |
---|
| 286 | _t._percent = step; |
---|
| 287 | |
---|
| 288 | // Perform easing |
---|
| 289 | if(_t.easing){ |
---|
| 290 | step = _t.easing(step); |
---|
| 291 | } |
---|
| 292 | |
---|
| 293 | _t._fire("onAnimate", [_t.curve.getValue(step)]); |
---|
| 294 | |
---|
| 295 | if(_t._percent < 1){ |
---|
| 296 | _t._startTimer(); |
---|
| 297 | }else{ |
---|
| 298 | _t._active = false; |
---|
| 299 | |
---|
| 300 | if(_t.repeat > 0){ |
---|
| 301 | _t.repeat--; |
---|
| 302 | _t.play(null, true); |
---|
| 303 | }else if(_t.repeat == -1){ |
---|
| 304 | _t.play(null, true); |
---|
| 305 | }else{ |
---|
| 306 | if(_t._startRepeatCount){ |
---|
| 307 | _t.repeat = _t._startRepeatCount; |
---|
| 308 | _t._startRepeatCount = 0; |
---|
| 309 | } |
---|
| 310 | } |
---|
| 311 | _t._percent = 0; |
---|
| 312 | _t._fire("onEnd", [_t.node]); |
---|
| 313 | !_t.repeat && _t._stopTimer(); |
---|
| 314 | } |
---|
| 315 | } |
---|
| 316 | return _t; // Animation |
---|
| 317 | }, |
---|
| 318 | |
---|
| 319 | _clearTimer: function(){ |
---|
| 320 | // summary: |
---|
| 321 | // Clear the play delay timer |
---|
| 322 | clearTimeout(this._delayTimer); |
---|
| 323 | delete this._delayTimer; |
---|
| 324 | } |
---|
| 325 | |
---|
| 326 | }); |
---|
| 327 | |
---|
| 328 | // the local timer, stubbed into all Animation instances |
---|
| 329 | var ctr = 0, |
---|
| 330 | timer = null, |
---|
| 331 | runner = { |
---|
| 332 | run: function(){} |
---|
| 333 | }; |
---|
| 334 | |
---|
| 335 | lang.extend(Animation, { |
---|
| 336 | |
---|
| 337 | _startTimer: function(){ |
---|
| 338 | if(!this._timer){ |
---|
| 339 | this._timer = aspect.after(runner, "run", lang.hitch(this, "_cycle"), true); |
---|
| 340 | ctr++; |
---|
| 341 | } |
---|
| 342 | if(!timer){ |
---|
| 343 | timer = setInterval(lang.hitch(runner, "run"), this.rate); |
---|
| 344 | } |
---|
| 345 | }, |
---|
| 346 | |
---|
| 347 | _stopTimer: function(){ |
---|
| 348 | if(this._timer){ |
---|
| 349 | this._timer.remove(); |
---|
| 350 | this._timer = null; |
---|
| 351 | ctr--; |
---|
| 352 | } |
---|
| 353 | if(ctr <= 0){ |
---|
| 354 | clearInterval(timer); |
---|
| 355 | timer = null; |
---|
| 356 | ctr = 0; |
---|
| 357 | } |
---|
| 358 | } |
---|
| 359 | |
---|
| 360 | }); |
---|
| 361 | |
---|
| 362 | var _makeFadeable = |
---|
| 363 | has("ie") ? function(node){ |
---|
| 364 | // only set the zoom if the "tickle" value would be the same as the |
---|
| 365 | // default |
---|
| 366 | var ns = node.style; |
---|
| 367 | // don't set the width to auto if it didn't already cascade that way. |
---|
| 368 | // We don't want to f anyones designs |
---|
| 369 | if(!ns.width.length && style.get(node, "width") == "auto"){ |
---|
| 370 | ns.width = "auto"; |
---|
| 371 | } |
---|
| 372 | } : |
---|
| 373 | function(){}; |
---|
| 374 | |
---|
| 375 | basefx._fade = function(/*Object*/ args){ |
---|
| 376 | // summary: |
---|
| 377 | // Returns an animation that will fade the node defined by |
---|
| 378 | // args.node from the start to end values passed (args.start |
---|
| 379 | // args.end) (end is mandatory, start is optional) |
---|
| 380 | |
---|
| 381 | args.node = dom.byId(args.node); |
---|
| 382 | var fArgs = _mixin({ properties: {} }, args), |
---|
| 383 | props = (fArgs.properties.opacity = {}); |
---|
| 384 | |
---|
| 385 | props.start = !("start" in fArgs) ? |
---|
| 386 | function(){ |
---|
| 387 | return +style.get(fArgs.node, "opacity")||0; |
---|
| 388 | } : fArgs.start; |
---|
| 389 | props.end = fArgs.end; |
---|
| 390 | |
---|
| 391 | var anim = basefx.animateProperty(fArgs); |
---|
| 392 | aspect.after(anim, "beforeBegin", lang.partial(_makeFadeable, fArgs.node), true); |
---|
| 393 | |
---|
| 394 | return anim; // Animation |
---|
| 395 | }; |
---|
| 396 | |
---|
| 397 | /*===== |
---|
| 398 | var __FadeArgs = declare(null, { |
---|
| 399 | // node: DOMNode|String |
---|
| 400 | // The node referenced in the animation |
---|
| 401 | // duration: Integer? |
---|
| 402 | // Duration of the animation in milliseconds. |
---|
| 403 | // easing: Function? |
---|
| 404 | // An easing function. |
---|
| 405 | }); |
---|
| 406 | =====*/ |
---|
| 407 | |
---|
| 408 | basefx.fadeIn = function(/*__FadeArgs*/ args){ |
---|
| 409 | // summary: |
---|
| 410 | // Returns an animation that will fade node defined in 'args' from |
---|
| 411 | // its current opacity to fully opaque. |
---|
| 412 | return basefx._fade(_mixin({ end: 1 }, args)); // Animation |
---|
| 413 | }; |
---|
| 414 | |
---|
| 415 | basefx.fadeOut = function(/*__FadeArgs*/ args){ |
---|
| 416 | // summary: |
---|
| 417 | // Returns an animation that will fade node defined in 'args' |
---|
| 418 | // from its current opacity to fully transparent. |
---|
| 419 | return basefx._fade(_mixin({ end: 0 }, args)); // Animation |
---|
| 420 | }; |
---|
| 421 | |
---|
| 422 | basefx._defaultEasing = function(/*Decimal?*/ n){ |
---|
| 423 | // summary: |
---|
| 424 | // The default easing function for Animation(s) |
---|
| 425 | return 0.5 + ((Math.sin((n + 1.5) * Math.PI)) / 2); // Decimal |
---|
| 426 | }; |
---|
| 427 | |
---|
| 428 | var PropLine = function(properties){ |
---|
| 429 | // PropLine is an internal class which is used to model the values of |
---|
| 430 | // an a group of CSS properties across an animation lifecycle. In |
---|
| 431 | // particular, the "getValue" function handles getting interpolated |
---|
| 432 | // values between start and end for a particular CSS value. |
---|
| 433 | this._properties = properties; |
---|
| 434 | for(var p in properties){ |
---|
| 435 | var prop = properties[p]; |
---|
| 436 | if(prop.start instanceof Color){ |
---|
| 437 | // create a reusable temp color object to keep intermediate results |
---|
| 438 | prop.tempColor = new Color(); |
---|
| 439 | } |
---|
| 440 | } |
---|
| 441 | }; |
---|
| 442 | |
---|
| 443 | PropLine.prototype.getValue = function(r){ |
---|
| 444 | var ret = {}; |
---|
| 445 | for(var p in this._properties){ |
---|
| 446 | var prop = this._properties[p], |
---|
| 447 | start = prop.start; |
---|
| 448 | if(start instanceof Color){ |
---|
| 449 | ret[p] = Color.blendColors(start, prop.end, r, prop.tempColor).toCss(); |
---|
| 450 | }else if(!lang.isArray(start)){ |
---|
| 451 | ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units || "px" : 0); |
---|
| 452 | } |
---|
| 453 | } |
---|
| 454 | return ret; |
---|
| 455 | }; |
---|
| 456 | |
---|
| 457 | /*===== |
---|
| 458 | var __AnimArgs = declare(__FadeArgs, { |
---|
| 459 | // properties: Object? |
---|
| 460 | // A hash map of style properties to Objects describing the transition, |
---|
| 461 | // such as the properties of _Line with an additional 'units' property |
---|
| 462 | properties: {} |
---|
| 463 | |
---|
| 464 | //TODOC: add event callbacks |
---|
| 465 | }); |
---|
| 466 | =====*/ |
---|
| 467 | |
---|
| 468 | basefx.animateProperty = function(/*__AnimArgs*/ args){ |
---|
| 469 | // summary: |
---|
| 470 | // Returns an animation that will transition the properties of |
---|
| 471 | // node defined in `args` depending how they are defined in |
---|
| 472 | // `args.properties` |
---|
| 473 | // |
---|
| 474 | // description: |
---|
| 475 | // Foundation of most `dojo/_base/fx` |
---|
| 476 | // animations. It takes an object of "properties" corresponding to |
---|
| 477 | // style properties, and animates them in parallel over a set |
---|
| 478 | // duration. |
---|
| 479 | // |
---|
| 480 | // example: |
---|
| 481 | // A simple animation that changes the width of the specified node. |
---|
| 482 | // | basefx.animateProperty({ |
---|
| 483 | // | node: "nodeId", |
---|
| 484 | // | properties: { width: 400 }, |
---|
| 485 | // | }).play(); |
---|
| 486 | // Dojo figures out the start value for the width and converts the |
---|
| 487 | // integer specified for the width to the more expressive but |
---|
| 488 | // verbose form `{ width: { end: '400', units: 'px' } }` which you |
---|
| 489 | // can also specify directly. Defaults to 'px' if omitted. |
---|
| 490 | // |
---|
| 491 | // example: |
---|
| 492 | // Animate width, height, and padding over 2 seconds... the |
---|
| 493 | // pedantic way: |
---|
| 494 | // | basefx.animateProperty({ node: node, duration:2000, |
---|
| 495 | // | properties: { |
---|
| 496 | // | width: { start: '200', end: '400', units:"px" }, |
---|
| 497 | // | height: { start:'200', end: '400', units:"px" }, |
---|
| 498 | // | paddingTop: { start:'5', end:'50', units:"px" } |
---|
| 499 | // | } |
---|
| 500 | // | }).play(); |
---|
| 501 | // Note 'paddingTop' is used over 'padding-top'. Multi-name CSS properties |
---|
| 502 | // are written using "mixed case", as the hyphen is illegal as an object key. |
---|
| 503 | // |
---|
| 504 | // example: |
---|
| 505 | // Plug in a different easing function and register a callback for |
---|
| 506 | // when the animation ends. Easing functions accept values between |
---|
| 507 | // zero and one and return a value on that basis. In this case, an |
---|
| 508 | // exponential-in curve. |
---|
| 509 | // | basefx.animateProperty({ |
---|
| 510 | // | node: "nodeId", |
---|
| 511 | // | // dojo figures out the start value |
---|
| 512 | // | properties: { width: { end: 400 } }, |
---|
| 513 | // | easing: function(n){ |
---|
| 514 | // | return (n==0) ? 0 : Math.pow(2, 10 * (n - 1)); |
---|
| 515 | // | }, |
---|
| 516 | // | onEnd: function(node){ |
---|
| 517 | // | // called when the animation finishes. The animation |
---|
| 518 | // | // target is passed to this function |
---|
| 519 | // | } |
---|
| 520 | // | }).play(500); // delay playing half a second |
---|
| 521 | // |
---|
| 522 | // example: |
---|
| 523 | // Like all `Animation`s, animateProperty returns a handle to the |
---|
| 524 | // Animation instance, which fires the events common to Dojo FX. Use `aspect.after` |
---|
| 525 | // to access these events outside of the Animation definition: |
---|
| 526 | // | var anim = basefx.animateProperty({ |
---|
| 527 | // | node:"someId", |
---|
| 528 | // | properties:{ |
---|
| 529 | // | width:400, height:500 |
---|
| 530 | // | } |
---|
| 531 | // | }); |
---|
| 532 | // | aspect.after(anim, "onEnd", function(){ |
---|
| 533 | // | console.log("animation ended"); |
---|
| 534 | // | }, true); |
---|
| 535 | // | // play the animation now: |
---|
| 536 | // | anim.play(); |
---|
| 537 | // |
---|
| 538 | // example: |
---|
| 539 | // Each property can be a function whose return value is substituted along. |
---|
| 540 | // Additionally, each measurement (eg: start, end) can be a function. The node |
---|
| 541 | // reference is passed directly to callbacks. |
---|
| 542 | // | basefx.animateProperty({ |
---|
| 543 | // | node:"mine", |
---|
| 544 | // | properties:{ |
---|
| 545 | // | height:function(node){ |
---|
| 546 | // | // shrink this node by 50% |
---|
| 547 | // | return domGeom.position(node).h / 2 |
---|
| 548 | // | }, |
---|
| 549 | // | width:{ |
---|
| 550 | // | start:function(node){ return 100; }, |
---|
| 551 | // | end:function(node){ return 200; } |
---|
| 552 | // | } |
---|
| 553 | // | } |
---|
| 554 | // | }).play(); |
---|
| 555 | // |
---|
| 556 | |
---|
| 557 | var n = args.node = dom.byId(args.node); |
---|
| 558 | if(!args.easing){ args.easing = dojo._defaultEasing; } |
---|
| 559 | |
---|
| 560 | var anim = new Animation(args); |
---|
| 561 | aspect.after(anim, "beforeBegin", lang.hitch(anim, function(){ |
---|
| 562 | var pm = {}; |
---|
| 563 | for(var p in this.properties){ |
---|
| 564 | // Make shallow copy of properties into pm because we overwrite |
---|
| 565 | // some values below. In particular if start/end are functions |
---|
| 566 | // we don't want to overwrite them or the functions won't be |
---|
| 567 | // called if the animation is reused. |
---|
| 568 | if(p == "width" || p == "height"){ |
---|
| 569 | this.node.display = "block"; |
---|
| 570 | } |
---|
| 571 | var prop = this.properties[p]; |
---|
| 572 | if(lang.isFunction(prop)){ |
---|
| 573 | prop = prop(n); |
---|
| 574 | } |
---|
| 575 | prop = pm[p] = _mixin({}, (lang.isObject(prop) ? prop: { end: prop })); |
---|
| 576 | |
---|
| 577 | if(lang.isFunction(prop.start)){ |
---|
| 578 | prop.start = prop.start(n); |
---|
| 579 | } |
---|
| 580 | if(lang.isFunction(prop.end)){ |
---|
| 581 | prop.end = prop.end(n); |
---|
| 582 | } |
---|
| 583 | var isColor = (p.toLowerCase().indexOf("color") >= 0); |
---|
| 584 | function getStyle(node, p){ |
---|
| 585 | // domStyle.get(node, "height") can return "auto" or "" on IE; this is more reliable: |
---|
| 586 | var v = { height: node.offsetHeight, width: node.offsetWidth }[p]; |
---|
| 587 | if(v !== undefined){ return v; } |
---|
| 588 | v = style.get(node, p); |
---|
| 589 | return (p == "opacity") ? +v : (isColor ? v : parseFloat(v)); |
---|
| 590 | } |
---|
| 591 | if(!("end" in prop)){ |
---|
| 592 | prop.end = getStyle(n, p); |
---|
| 593 | }else if(!("start" in prop)){ |
---|
| 594 | prop.start = getStyle(n, p); |
---|
| 595 | } |
---|
| 596 | |
---|
| 597 | if(isColor){ |
---|
| 598 | prop.start = new Color(prop.start); |
---|
| 599 | prop.end = new Color(prop.end); |
---|
| 600 | }else{ |
---|
| 601 | prop.start = (p == "opacity") ? +prop.start : parseFloat(prop.start); |
---|
| 602 | } |
---|
| 603 | } |
---|
| 604 | this.curve = new PropLine(pm); |
---|
| 605 | }), true); |
---|
| 606 | aspect.after(anim, "onAnimate", lang.hitch(style, "set", anim.node), true); |
---|
| 607 | return anim; // Animation |
---|
| 608 | }; |
---|
| 609 | |
---|
| 610 | basefx.anim = function( /*DOMNode|String*/ node, |
---|
| 611 | /*Object*/ properties, |
---|
| 612 | /*Integer?*/ duration, |
---|
| 613 | /*Function?*/ easing, |
---|
| 614 | /*Function?*/ onEnd, |
---|
| 615 | /*Integer?*/ delay){ |
---|
| 616 | // summary: |
---|
| 617 | // A simpler interface to `animateProperty()`, also returns |
---|
| 618 | // an instance of `Animation` but begins the animation |
---|
| 619 | // immediately, unlike nearly every other Dojo animation API. |
---|
| 620 | // description: |
---|
| 621 | // Simpler (but somewhat less powerful) version |
---|
| 622 | // of `animateProperty`. It uses defaults for many basic properties |
---|
| 623 | // and allows for positional parameters to be used in place of the |
---|
| 624 | // packed "property bag" which is used for other Dojo animation |
---|
| 625 | // methods. |
---|
| 626 | // |
---|
| 627 | // The `Animation` object returned will be already playing, so |
---|
| 628 | // calling play() on it again is (usually) a no-op. |
---|
| 629 | // node: |
---|
| 630 | // a DOM node or the id of a node to animate CSS properties on |
---|
| 631 | // duration: |
---|
| 632 | // The number of milliseconds over which the animation |
---|
| 633 | // should run. Defaults to the global animation default duration |
---|
| 634 | // (350ms). |
---|
| 635 | // easing: |
---|
| 636 | // An easing function over which to calculate acceleration |
---|
| 637 | // and deceleration of the animation through its duration. |
---|
| 638 | // A default easing algorithm is provided, but you may |
---|
| 639 | // plug in any you wish. A large selection of easing algorithms |
---|
| 640 | // are available in `dojo/fx/easing`. |
---|
| 641 | // onEnd: |
---|
| 642 | // A function to be called when the animation finishes |
---|
| 643 | // running. |
---|
| 644 | // delay: |
---|
| 645 | // The number of milliseconds to delay beginning the |
---|
| 646 | // animation by. The default is 0. |
---|
| 647 | // example: |
---|
| 648 | // Fade out a node |
---|
| 649 | // | basefx.anim("id", { opacity: 0 }); |
---|
| 650 | // example: |
---|
| 651 | // Fade out a node over a full second |
---|
| 652 | // | basefx.anim("id", { opacity: 0 }, 1000); |
---|
| 653 | return basefx.animateProperty({ // Animation |
---|
| 654 | node: node, |
---|
| 655 | duration: duration || Animation.prototype.duration, |
---|
| 656 | properties: properties, |
---|
| 657 | easing: easing, |
---|
| 658 | onEnd: onEnd |
---|
| 659 | }).play(delay || 0); |
---|
| 660 | }; |
---|
| 661 | |
---|
| 662 | |
---|
| 663 | if(has("extend-dojo")){ |
---|
| 664 | _mixin(dojo, basefx); |
---|
| 665 | // Alias to drop come 2.0: |
---|
| 666 | dojo._Animation = Animation; |
---|
| 667 | } |
---|
| 668 | |
---|
| 669 | return basefx; |
---|
| 670 | }); |
---|