Advertisement
Guest User

FBJS Animation

a guest
May 10th, 2010
530
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* Copyright (c) 2008, Marcel Laverdet and Facebook, inc.
  2.  * All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions are met:
  6.  *    * Redistributions of source code must retain the above copyright
  7.  *      notice, this list of conditions and the following disclaimer.
  8.  *    * Redistributions in binary form must reproduce the above copyright
  9.  *      notice, this list of conditions and the following disclaimer in the
  10.  *      documentation and/or other materials provided with the distribution.
  11.  *    * Neither the name of Facebook, inc. nor the names of its contributors
  12.  *      may be used to endorse or promote products derived from this software
  13.  *      without specific prior written permission.
  14.  *
  15.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY
  16.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  17.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18.  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
  19.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  22.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  24.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
  25. function Animation(obj) {
  26.   if (this == window) {
  27.     return new Animation(obj);
  28.   } else {
  29.     this.obj = obj;
  30.     this._reset_state();
  31.     this.queue = [];
  32.     this.last_attr = null;
  33.   }
  34. }
  35. Animation.resolution = 20;
  36. Animation.offset = 0;
  37.  
  38. // Initializes the state to blank values
  39. Animation.prototype._reset_state = function() {
  40.   this.state = {
  41.     attrs: {},
  42.     duration: 500 // default duration
  43.   }
  44. }
  45.  
  46. // Stops any current animation
  47. Animation.prototype.stop = function() {
  48.   this._reset_state();
  49.   this.queue = [];
  50.   return this;
  51. }
  52.  
  53. // Builds an overflow:hidden container for this.obj. Used with .blind()
  54. Animation.prototype._build_container = function() {
  55.   if (this.container_div) {
  56.     this._refresh_container();
  57.     return;
  58.   }
  59.   // ref-counting here on the magic container in case someone decides to start two animations with blind() on the same element
  60.   // Either way it's not ideal because if animating to 'auto' the calculations will probably be incorrect... but at least this
  61.   // way it won't tear up your DOM.
  62.   if (this.obj.firstChild && this.obj.firstChild.__animation_refs) {
  63.     this.container_div = this.obj.firstChild;
  64.     this.container_div.__animation_refs++;
  65.     this._refresh_container();
  66.     return;
  67.   }
  68.   var container = document.createElement('div');
  69.   container.style.padding = '0px';
  70.   container.style.margin = '0px';
  71.   container.style.border = '0px';
  72.   container.__animation_refs = 1;
  73.   var children = this.obj.childNodes;
  74.   while (children.length) {
  75.     container.appendChild(children[0]);
  76.   }
  77.   this.obj.appendChild(container);
  78.   this.obj.style.overflow = 'hidden';
  79.   this.container_div = container;
  80.   this._refresh_container();
  81. }
  82.  
  83. // Refreshes the size of the container. Used on checkpoints and such.
  84. Animation.prototype._refresh_container = function() {
  85.   this.container_div.style.height = 'auto';
  86.   this.container_div.style.width = 'auto';
  87.   this.container_div.style.height = this.container_div.offsetHeight+'px';
  88.   this.container_div.style.width = this.container_div.offsetWidth+'px';
  89. }
  90.  
  91. // Destroys the container built by _build_container()
  92. Animation.prototype._destroy_container = function() {
  93.   if (!this.container_div) {
  94.     return;
  95.   }
  96.   if (!--this.container_div.__animation_refs) {
  97.     var children = this.container_div.childNodes;
  98.     while (children.length) {
  99.       this.obj.appendChild(children[0]);
  100.     }
  101.     this.obj.removeChild(this.container_div);
  102.   }
  103.   this.container_div = null;
  104. }
  105.  
  106. // Generalized attr function. Calls to .to, .by, and .from go through this
  107. Animation.ATTR_TO = 1;
  108. Animation.ATTR_BY = 2;
  109. Animation.ATTR_FROM = 3;
  110. Animation.prototype._attr = function(attr, value, mode) {
  111.  
  112.   // Turn stuff like border-left into borderLeft
  113.   attr = attr.replace(/-[a-z]/gi, function(l) {
  114.     return l.substring(1).toUpperCase();
  115.   });
  116.  
  117.   var auto = false;
  118.   switch (attr) {
  119.     case 'background':
  120.       this._attr('backgroundColor', value, mode);
  121.       return this;
  122.  
  123.     case 'margin':
  124.       value = Animation.parse_group(value);
  125.       this._attr('marginBottom', value[0], mode);
  126.       this._attr('marginLeft', value[1], mode);
  127.       this._attr('marginRight', value[2], mode);
  128.       this._attr('marginTop', value[3], mode);
  129.       return this;
  130.  
  131.     case 'padding':
  132.       value = Animation.parse_group(value);
  133.       this._attr('paddingBottom', value[0], mode);
  134.       this._attr('paddingLeft', value[1], mode);
  135.       this._attr('paddingRight', value[2], mode);
  136.       this._attr('paddingTop', value[3], mode);
  137.       return this;
  138.  
  139.     case 'backgroundColor':
  140.     case 'borderColor':
  141.     case 'color':
  142.       value = Animation.parse_color(value);
  143.       break;
  144.  
  145.     case 'opacity':
  146.       value = parseFloat(value, 10);
  147.       break;
  148.  
  149.     case 'height':
  150.     case 'width':
  151.       if (value == 'auto') {
  152.         auto = true;
  153.       } else {
  154.         value = parseInt(value, 10);
  155.       }
  156.       break;
  157.  
  158.     case 'borderWidth':
  159.     case 'lineHeight':
  160.     case 'fontSize':
  161.     case 'marginBottom':
  162.     case 'marginLeft':
  163.     case 'marginRight':
  164.     case 'marginTop':
  165.     case 'paddingBottom':
  166.     case 'paddingLeft':
  167.     case 'paddingRight':
  168.     case 'paddingTop':
  169.     case 'bottom':
  170.     case 'left':
  171.     case 'right':
  172.     case 'top':
  173.     case 'scrollTop':
  174.     case 'scrollLeft':
  175.       value = parseInt(value, 10);
  176.       break;
  177.  
  178.     default:
  179.       throw new Error(attr+' is not a supported attribute!');
  180.   }
  181.  
  182.   if (this.state.attrs[attr] === undefined) {
  183.     this.state.attrs[attr] = {};
  184.   }
  185.   if (auto) {
  186.     this.state.attrs[attr].auto = true;
  187.   }
  188.   switch (mode) {
  189.     case Animation.ATTR_FROM:
  190.       this.state.attrs[attr].start = value;
  191.       break;
  192.  
  193.     case Animation.ATTR_BY:
  194.       this.state.attrs[attr].by = true;
  195.       // fall through
  196.  
  197.     case Animation.ATTR_TO:
  198.       this.state.attrs[attr].value = value;
  199.       break;
  200.   }
  201. }
  202.  
  203. // Explcit animation to a certain value
  204. Animation.prototype.to = function(attr, value) {
  205.   if (value === undefined) {
  206.     this._attr(this.last_attr, attr, Animation.ATTR_TO);
  207.   } else {
  208.     this._attr(attr, value, Animation.ATTR_TO);
  209.     this.last_attr = attr;
  210.   }
  211.   return this;
  212. }
  213.  
  214. // Animation by a value (i.e. add this value to the current value)
  215. Animation.prototype.by = function(attr, value) {
  216.   if (value === undefined) {
  217.     this._attr(this.last_attr, attr, Animation.ATTR_BY);
  218.   } else {
  219.     this._attr(attr, value, Animation.ATTR_BY);
  220.     this.last_attr = attr;
  221.   }
  222.   return this;
  223. }
  224.  
  225. // Start the animation from a value instead of the current value
  226. Animation.prototype.from = function(attr, value) {
  227.   if (value === undefined) {
  228.     this._attr(this.last_attr, attr, Animation.ATTR_FROM);
  229.   } else {
  230.     this._attr(attr, value, Animation.ATTR_FROM);
  231.     this.last_attr = attr;
  232.   }
  233.   return this;
  234. }
  235.  
  236. // How long is this animation supposed to last (in miliseconds)
  237. Animation.prototype.duration = function(duration) {
  238.   this.state.duration = duration ? duration : 0;
  239.   return this;
  240. }
  241.  
  242. // Checkpoint the animation to start a new one.
  243. Animation.prototype.checkpoint = function(distance /* = 1.0 */, callback) {
  244.   if (distance === undefined) {
  245.     distance = 1;
  246.   }
  247.   this.state.checkpoint = distance;
  248.   this.state.checkpointcb = callback;
  249.   this.queue.push(this.state);
  250.   this._reset_state();
  251.   return this;
  252. }
  253.  
  254. // This animation requires an overflow container (usually used for width animations)
  255. Animation.prototype.blind = function() {
  256.   this.state.blind = true;
  257.   return this;
  258. }
  259.  
  260. // Hide this object at the end of the animation
  261. Animation.prototype.hide = function() {
  262.   this.state.hide = true;
  263.   return this;
  264. }
  265.  
  266. // Show this object at the beginning of the animation
  267. Animation.prototype.show = function() {
  268.   this.state.show = true;
  269.   return this;
  270. }
  271.  
  272. // Use an easing function to adjust the distribution of the animation state over frames
  273. Animation.prototype.ease = function(ease) {
  274.   this.state.ease = ease;
  275.   return this;
  276. }
  277.  
  278. // Let the animation begin!
  279. Animation.prototype.go = function() {
  280.   var time = (new Date()).getTime();
  281.   this.queue.push(this.state);
  282.  
  283.   for (var i = 0; i < this.queue.length; i++) {
  284.     this.queue[i].start = time - Animation.offset;
  285.     if (this.queue[i].checkpoint) {
  286.       time += this.queue[i].checkpoint * this.queue[i].duration;
  287.     }
  288.   }
  289.   Animation.push(this);
  290.   return this;
  291. }
  292.  
  293. // Draw a frame for this animation
  294. Animation.prototype._frame = function(time) {
  295.   var done = true;
  296.   var still_needs_container = false;
  297.   var whacky_firefox = false;
  298.   for (var i = 0; i < this.queue.length; i++) {
  299.  
  300.     // If this animation shouldn't start yet we can abort early
  301.     var cur = this.queue[i];
  302.     if (cur.start > time) {
  303.       done = false;
  304.       continue;
  305.     } else if (cur.checkpointcb && (cur.checkpoint * cur.duration + cur.start > time)) {
  306.       this._callback(cur.checkpointcb, time - cur.start - cur.checkpoint * cur.duration);
  307.       cur.checkpointcb = null;
  308.     }
  309.  
  310.     // We need to initialize an animation on the first frame
  311.     if (cur.started === undefined) {
  312.       if (cur.show) {
  313.         this.obj.style.display = 'block';
  314.       }
  315.       for (var a in cur.attrs) {
  316.         if (cur.attrs[a].start !== undefined) {
  317.           continue;
  318.         }
  319.         switch (a) {
  320.           case 'backgroundColor':
  321.           case 'borderColor':
  322.           case 'color':
  323.             // Defer to the left border color, whatever.
  324.             var val = Animation.parse_color(Animation.get_style(this.obj, a == 'borderColor' ? 'borderLeftColor' : a));
  325.  
  326.             // I'm not sure why anyone would want to do relative color adjustment... but at least they can
  327.             if (cur.attrs[a].by) {
  328.               cur.attrs[a].value[0] = Math.min(255, Math.max(0, cur.attrs[a].value[0] + val[0]));
  329.               cur.attrs[a].value[1] = Math.min(255, Math.max(0, cur.attrs[a].value[1] + val[1]));
  330.               cur.attrs[a].value[2] = Math.min(255, Math.max(0, cur.attrs[a].value[2] + val[2]));
  331.             }
  332.             break;
  333.  
  334.           case 'opacity':
  335.             var val = ((val = Animation.get_style(this.obj, 'opacity')) && parseFloat(val)) ||
  336.                       ((val = Animation.get_style(this.obj, 'opacity')) && (val = /(\d+(?:\.\d+)?)/.exec(val)) && parseFloat(val.pop()) / 100) ||
  337.                       1;
  338.             if (cur.attrs[a].by) {
  339.               cur.attrs[a].value = Math.min(1, Math.max(0, cur.attrs[a].value + val));
  340.             }
  341.             break;
  342.  
  343.           case 'height':
  344.           case 'width':
  345.             var val = Animation['get_'+a](this.obj);
  346.             if (cur.attrs[a].by) {
  347.               cur.attrs[a].value += val;
  348.             }
  349.             break;
  350.  
  351.           case 'scrollLeft':
  352.           case 'scrollTop':
  353.             var val = (this.obj == document.body) ? (document.documentElement[a] || document.body[a]) : this.obj[a];
  354.             if (cur.attrs[a].by) {
  355.               cur.attrs[a].value += val;
  356.             }
  357.             cur['last'+a] = val;
  358.             break;
  359.  
  360.           default:
  361.             var val = parseInt(Animation.get_style(this.obj, a), 10);
  362.             if (cur.attrs[a].by) {
  363.               cur.attrs[a].value += val;
  364.             }
  365.             break;
  366.         }
  367.         cur.attrs[a].start = val;
  368.       }
  369.  
  370.       // If we're animating height or width to "auto" we need to do some DOM-fu to figure out what that means in px
  371.       if ((cur.attrs.height && cur.attrs.height.auto) ||
  372.           (cur.attrs.width && cur.attrs.width.auto)) {
  373.  
  374.         // This is a silly fix for Firefox's whacky redrawing. This bug can be reproduced with the following code:
  375.         /* <div style="width: 300px; height: 300px; background: red" id="test"></div>
  376.            <script>
  377.              setInterval(function() {
  378.                var obj = document.getElementById('test');
  379.            //    var p = obj.parentNode;
  380.                obj.style.visibility = 'hidden';
  381.                obj.offsetWidth; // request a property, requires a re-render
  382.            //    p.removeChild(obj);
  383.                obj.style.visibility = 'visible';
  384.            //    p.appendChild(obj);
  385.              }, 100);
  386.            </script> */
  387.         // In all browsers that aren't Firefox the div will stay solid red. In Firefox it flickers. The workaround
  388.         // for the bug is included in the code block above, but commented out.
  389.         if (/Firefox\/[12]\./.test(navigator.userAgent)) {
  390.           whacky_firefox = true;
  391.         }
  392.  
  393.         // Set any attributes that affect element size to their final desired values
  394.         this._destroy_container();
  395.         for (var a in {height: 1, width: 1,
  396.                        fontSize: 1,
  397.                        borderLeftWidth: 1, borderRightWidth: 1, borderTopWidth: 1, borderBottomWidth: 1,
  398.                        paddingLeft: 1, paddingRight: 1, paddingTop: 1, paddingBottom: 1}) {
  399.           if (cur.attrs[a]) {
  400.             this.obj.style[a] = cur.attrs[a].value + (typeof cur.attrs[a].value == 'number' ? 'px' : '');
  401.           }
  402.         }
  403.  
  404.         // Record the dimensions of what the element will look like after the animation
  405.         if (cur.attrs.height && cur.attrs.height.auto) {
  406.           cur.attrs.height.value = Animation.get_height(this.obj);
  407.         }
  408.         if (cur.attrs.width && cur.attrs.width.auto) {
  409.           cur.attrs.width.value = Animation.get_width(this.obj);
  410.         }
  411.  
  412.         // We don't need to do anything else with temporarily adjusted style because they're
  413.         // about to be overwritten in the frame loop below
  414.       }
  415.  
  416.       cur.started = true;
  417.       if (cur.blind) {
  418.         this._build_container();
  419.       }
  420.     }
  421.  
  422.     // Calculate the animation's progress from 0 - 1
  423.     var p = (time - cur.start) / cur.duration;
  424.     if (p >= 1) {
  425.       p = 1;
  426.       if (cur.hide) {
  427.         this.obj.style.display = 'none';
  428.       }
  429.     } else {
  430.       done = false;
  431.     }
  432.     var pc = cur.ease ? cur.ease(p) : p;
  433.  
  434.     // If this needs a blind container and doesn't have one, we build it
  435.     if (!still_needs_container && p != 1 && cur.blind) {
  436.       still_needs_container = true;
  437.     }
  438.  
  439.     // Hack documented above
  440.     if (whacky_firefox && this.obj.parentNode) {
  441.       var parentNode = this.obj.parentNode;
  442.       var nextChild = this.obj.nextSibling;
  443.       parentNode.removeChild(this.obj);
  444.     }
  445.  
  446.     // Loop through each animated attribute and set it where it needs to be
  447.     for (var a in cur.attrs) {
  448.       switch (a) {
  449.         case 'backgroundColor':
  450.         case 'borderColor':
  451.         case 'color':
  452.           this.obj.style[a] = 'rgb('+
  453.             Animation.calc_tween(pc, cur.attrs[a].start[0], cur.attrs[a].value[0], true)+','+
  454.             Animation.calc_tween(pc, cur.attrs[a].start[1], cur.attrs[a].value[1], true)+','+
  455.             Animation.calc_tween(pc, cur.attrs[a].start[2], cur.attrs[a].value[2], true)+')';
  456.           break;
  457.  
  458.         case 'opacity':
  459.           var opacity = Animation.calc_tween(pc, cur.attrs[a].start, cur.attrs[a].value);
  460.           try {
  461.             this.obj.style.opacity = (opacity == 1 ? '' : opacity);
  462.             this.obj.style.filter = (opacity == 1 ? '' : 'alpha(opacity=' + opacity * 100 + ')');
  463.           }
  464.           catch (e) {}
  465.           break;
  466.  
  467.         case 'height':
  468.         case 'width':
  469.           this.obj.style[a] = pc == 1 && cur.attrs[a].auto ? 'auto' :
  470.                               Animation.calc_tween(pc, cur.attrs[a].start, cur.attrs[a].value, true)+'px';
  471.           break;
  472.  
  473.         case 'scrollLeft':
  474.         case 'scrollTop':
  475.           // Special-case here for scrolling. If the user overrides the scroll we immediately terminate this animation
  476.           var val = (this.obj == document.body) ? (document.documentElement[a] || document.body[a]) : this.obj[a];
  477.           if (cur['last'+a] != val) {
  478.             delete cur.attrs[a];
  479.           } else {
  480.             var diff = Animation.calc_tween(pc, cur.attrs[a].start, cur.attrs[a].value, true) - val;
  481.             if (a == 'scrollLeft') {
  482.               window.scrollBy(diff, 0);
  483.             } else {
  484.               window.scrollBy(0, diff);
  485.             }
  486.             cur['last'+a] = diff + val;
  487.           }
  488.           break;
  489.  
  490.         default:
  491.           this.obj.style[a] = Animation.calc_tween(pc, cur.attrs[a].start, cur.attrs[a].value, true)+'px';
  492.           break;
  493.       }
  494.     }
  495.  
  496.     // If this animation is complete remove it from the queue
  497.     if (p == 1) {
  498.       this.queue.splice(i--, 1);
  499.       this._callback(cur.ondone, time - cur.start - cur.duration);
  500.     }
  501.   }
  502.  
  503.   // Hack documented above
  504.   if (whacky_firefox) {
  505.     parentNode[nextChild ? 'insertBefore' : 'appendChild'](this.obj, nextChild);
  506.   }
  507.  
  508.   if (!still_needs_container && this.container_div) {
  509.     this._destroy_container();
  510.   }
  511.   return !done;
  512. }
  513.  
  514. // Add a callback to fire when this animation is finished
  515. Animation.prototype.ondone = function(fn) {
  516.   this.state.ondone = fn;
  517.   return this;
  518. }
  519.  
  520. // Call a callback with a time offset (for instantiating more animations)
  521. Animation.prototype._callback = function(callback, offset) {
  522.   if (callback) {
  523.     Animation.offset = offset;
  524.     callback.call(this);
  525.     Animation.offset = 0;
  526.   }
  527. }
  528.  
  529. // Calculates a value in between two values based on a percentage. Basically a weighted average.
  530. Animation.calc_tween = function(p, v1, v2, whole) {
  531.   return (whole ? parseInt : parseFloat)((v2 - v1) * p + v1, 10);
  532. }
  533.  
  534. // Takes a color like #fff and returns an array of [255, 255, 255].
  535. Animation.parse_color = function(color) {
  536.   var hex = /^#([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i.exec(color);
  537.   if (hex) {
  538.     return [parseInt(hex[1].length == 1 ? hex[1] + hex[1] : hex[1], 16),
  539.             parseInt(hex[2].length == 1 ? hex[2] + hex[2] : hex[2], 16),
  540.             parseInt(hex[3].length == 1 ? hex[3] + hex[3] : hex[3], 16)];
  541.   } else {
  542.     var rgb = /^rgba? *\(([0-9]+), *([0-9]+), *([0-9]+)(?:, *([0-9]+))?\)$/.exec(color);
  543.     if (rgb) {
  544.       if (rgb[4] === '0') {
  545.         return [255, 255, 255]; // transparent
  546.       } else {
  547.         return [parseInt(rgb[1], 10), parseInt(rgb[2], 10), parseInt(rgb[3], 10)];
  548.       }
  549.     } else if (color == 'transparent') {
  550.       return [255, 255, 255]; // not much we can do here...
  551.     } else {
  552.       // When we open this to Platform we'll need a key-value list of names to rgb values
  553.       throw 'Named color attributes are not supported.';
  554.     }
  555.   }
  556. }
  557.  
  558. // Takes a CSS attribute like padding or margin and returns an explicit array of 4 values
  559. // Ex: '0px 1px' -> ['0px', '1px', '0px', '1px']
  560. Animation.parse_group = function(value) {
  561.   var value = trim(value).split(/ +/);
  562.   if (value.length == 4) {
  563.     return value;
  564.   } else if (value.length == 3) {
  565.     return [value[0], value[1], value[2], value[1]];
  566.   } else if (value.length == 2) {
  567.     return [value[0], value[1], value[0], value[1]];
  568.   } else {
  569.     return [value[0], value[0], value[0], value[0]];
  570.   }
  571. }
  572.  
  573. // Gets the current height of an element which when used with obj.style.height = height+'px' is a visual NO-OP
  574. Animation.get_height = function(obj) {
  575.   var pT = parseInt(Animation.get_style(obj, 'paddingTop'), 10),
  576.       pB = parseInt(Animation.get_style(obj, 'paddingBottom'), 10),
  577.       bT = parseInt(Animation.get_style(obj, 'borderTopWidth'), 10),
  578.       bW = parseInt(Animation.get_style(obj, 'borderBottomWidth'), 10);
  579.   return obj.offsetHeight - (pT ? pT : 0) - (pB ? pB : 0) - (bT ? bT : 0) - (bW ? bW : 0);
  580. }
  581.  
  582. // Similar to get_height except for widths
  583. Animation.get_width = function(obj) {
  584.   var pL = parseInt(Animation.get_style(obj, 'paddingLeft'), 10),
  585.       pR = parseInt(Animation.get_style(obj, 'paddingRight'), 10),
  586.       bL = parseInt(Animation.get_style(obj, 'borderLeftWidth'), 10),
  587.       bR = parseInt(Animation.get_style(obj, 'borderRightWidth'), 10);
  588.   return obj.offsetWidth - (pL ? pL : 0) - (pR ? pR : 0) - (bL ? bL : 0) - (bR ? bR : 0);
  589. }
  590.  
  591. // Gets the computed style of an element
  592. Animation.get_style = function(obj, prop) {
  593.   var temp;
  594.   return (window.getComputedStyle && window.getComputedStyle(obj, null).getPropertyValue(prop.replace(/[A-Z]/g, function(match) { return '-' + match.toLowerCase() }))) ||
  595.          (document.defaultView && document.defaultView.getComputedStyle && (temp = document.defaultView.getComputedStyle(obj, null)) && temp.getPropertyValue(prop.replace(/[A-Z]/g, function(match) { return '-' + match.toLowerCase() }))) ||
  596.          (obj.currentStyle && obj.currentStyle[prop]) ||
  597.          obj.style[prop];
  598. }
  599.  
  600. // Add this animation object to the global animation stack.
  601. Animation.push = function(instance) {
  602.   if (!Animation.active) {
  603.     Animation.active = [];
  604.   }
  605.   Animation.active.push(instance);
  606.   if (!Animation.timeout) {
  607.     Animation.timeout = setInterval(Animation.animate, Animation.resolution);
  608.   }
  609. }
  610.  
  611. // Renders a frame from each naimation currently active. By putting all our animations in one
  612. // stack it gives us the advantage of a single setInterval with all style updates in a single
  613. // callback. That means the browser will do less rendering and multiple animations will be
  614. // smoother.
  615. Animation.animate = function() {
  616.   var done = true;
  617.   var time = (new Date()).getTime();
  618.   for (var i = 0; i < Animation.active.length; i++) {
  619.     if (Animation.active[i]._frame(time)) {
  620.       done = false;
  621.     } else {
  622.       Animation.active.splice(i--, 1); // remove from the list
  623.     }
  624.   }
  625.   if (done) {
  626.     clearInterval(Animation.timeout);
  627.     Animation.timeout = null;
  628.   }
  629. }
  630.  
  631. // Ease functions. These functions all have a domain and (maybe) range of 0 - 1
  632. Animation.ease = {}
  633. Animation.ease.begin = function(p) {
  634.   return p * p;
  635. }
  636. Animation.ease.end = function(p) {
  637.   p -= 1;
  638.   return -(p * p) + 1;
  639. }
  640. Animation.ease.both = function(p) {
  641.   if (p <= 0.5) {
  642.     return (p * p) * 2;
  643.   } else {
  644.     p -= 1;
  645.     return (p * p) * -2 + 1;
  646.   }
  647. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement