diff options
| author | Matias Niemelä | 2014-02-24 19:43:57 -0500 | 
|---|---|---|
| committer | Matias Niemelä | 2014-02-24 21:23:18 -0500 | 
| commit | 4c4537e65e6cf911c9659b562d89e3330ce3ffae (patch) | |
| tree | 3f9e6736a314073b5516beab3c86d7709e887b6b | |
| parent | 62761428eff3a53e69367449eb81869e59e75e39 (diff) | |
| download | angular.js-4c4537e65e6cf911c9659b562d89e3330ce3ffae.tar.bz2 | |
perf($animate): use rAF instead of timeouts to issue animation callbacks
| -rw-r--r-- | src/ng/animate.js | 16 | ||||
| -rw-r--r-- | src/ngAnimate/animate.js | 54 | ||||
| -rw-r--r-- | src/ngMock/angular-mocks.js | 15 | ||||
| -rw-r--r-- | test/ng/directive/ngClassSpec.js | 5 | ||||
| -rw-r--r-- | test/ng/directive/ngIncludeSpec.js | 14 | ||||
| -rw-r--r-- | test/ngAnimate/animateSpec.js | 153 | ||||
| -rw-r--r-- | test/ngRoute/directive/ngViewSpec.js | 12 | 
7 files changed, 143 insertions, 126 deletions
| diff --git a/src/ng/animate.js b/src/ng/animate.js index 130e61e7..d90f086b 100644 --- a/src/ng/animate.js +++ b/src/ng/animate.js @@ -81,7 +81,11 @@ var $AnimateProvider = ['$provide', function($provide) {      return this.$$classNameFilter;    }; -  this.$get = ['$timeout', function($timeout) { +  this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) { + +    function async(fn) { +      fn && $$asyncCallback(fn); +    }      /**       * @@ -126,7 +130,7 @@ var $AnimateProvider = ['$provide', function($provide) {            }            parent.append(element);          } -        done && $timeout(done, 0, false); +        async(done);        },        /** @@ -142,7 +146,7 @@ var $AnimateProvider = ['$provide', function($provide) {         */        leave : function(element, done) {          element.remove(); -        done && $timeout(done, 0, false); +        async(done);        },        /** @@ -189,7 +193,7 @@ var $AnimateProvider = ['$provide', function($provide) {          forEach(element, function (element) {            jqLiteAddClass(element, className);          }); -        done && $timeout(done, 0, false); +        async(done);        },        /** @@ -212,7 +216,7 @@ var $AnimateProvider = ['$provide', function($provide) {          forEach(element, function (element) {            jqLiteRemoveClass(element, className);          }); -        done && $timeout(done, 0, false); +        async(done);        },        /** @@ -234,7 +238,7 @@ var $AnimateProvider = ['$provide', function($provide) {            jqLiteAddClass(element, add);            jqLiteRemoveClass(element, remove);          }); -        done && $timeout(done, 0, false); +        async(done);        },        enabled : noop diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index 5c0ed917..6ccf6a5d 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -247,42 +247,24 @@ angular.module('ngAnimate', ['ng'])     * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.     *     */ -  .factory('$$animateReflow', ['$window', '$timeout', '$document', -                       function($window,   $timeout,   $document) { + +  //this private service is only used within CSS-enabled animations +  //IE8 + IE9 do not support rAF natively, but that is fine since they +  //also don't support transitions and keyframes which means that the code +  //below will never be used by the two browsers. +  .factory('$$animateReflow', ['$$rAF', '$document', function($$rAF, $document) {      var bod = $document[0].body; -    var requestAnimationFrame = $window.requestAnimationFrame       || -                                $window.webkitRequestAnimationFrame || -                                function(fn) { -                                  return $timeout(fn, 10, false); -                                }; - -    var cancelAnimationFrame = $window.cancelAnimationFrame       || -                               $window.webkitCancelAnimationFrame || -                               function(timer) { -                                 return $timeout.cancel(timer); -                               };      return function(fn) { -      var id = requestAnimationFrame(function() { +      //the returned function acts as the cancellation function +      return $$rAF(function() { +        //the line below will force the browser to perform a repaint +        //so that all the animated elements within the animation frame +        //will be properly updated and drawn on screen. This is +        //required to perform multi-class CSS based animations with +        //Firefox. DO NOT REMOVE THIS LINE.          var a = bod.offsetWidth + 1;          fn();        }); -      return function() { -        cancelAnimationFrame(id); -      }; -    }; -  }]) - -  .factory('$$asyncQueueBuffer', ['$timeout', function($timeout) { -    var timer, queue = []; -    return function(fn) { -      $timeout.cancel(timer); -      queue.push(fn); -      timer = $timeout(function() { -        for(var i = 0; i < queue.length; i++) { -          queue[i](); -        } -        queue = []; -      }, 0, false);      };    }]) @@ -313,8 +295,8 @@ angular.module('ngAnimate', ['ng'])        return extractElementNode(elm1) == extractElementNode(elm2);      } -    $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncQueueBuffer', '$rootScope', '$document', -                            function($delegate,   $injector,   $sniffer,   $rootElement,   $$asyncQueueBuffer,    $rootScope,   $document) { +    $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document', +                            function($delegate,   $injector,   $sniffer,   $rootElement,   $$asyncCallback,    $rootScope,   $document) {        var globalAnimationCounter = 0;        $rootElement.data(NG_ANIMATE_STATE, rootAnimateState); @@ -876,7 +858,7 @@ angular.module('ngAnimate', ['ng'])          function fireDOMCallback(animationPhase) {            var eventName = '$animate:' + animationPhase;            if(elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) { -            $$asyncQueueBuffer(function() { +            $$asyncCallback(function() {                element.triggerHandler(eventName, {                  event : animationEvent,                  className : className @@ -896,7 +878,7 @@ angular.module('ngAnimate', ['ng'])          function fireDoneCallbackAsync() {            fireDOMCallback('close');            if(doneCallback) { -            $$asyncQueueBuffer(function() { +            $$asyncCallback(function() {                doneCallback();              });            } @@ -923,7 +905,7 @@ angular.module('ngAnimate', ['ng'])                if(isClassBased) {                  cleanup(element, className);                } else { -                $$asyncQueueBuffer(function() { +                $$asyncCallback(function() {                    var data = element.data(NG_ANIMATE_STATE) || {};                    if(localAnimationCount == data.index) {                      cleanup(element, className, animationEvent); diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index bcd6cc1f..c9f31431 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -757,21 +757,24 @@ angular.mock.TzDate.prototype = Date.prototype;  angular.mock.animate = angular.module('ngAnimateMock', ['ng'])    .config(['$provide', function($provide) { -    var reflowQueue = []; +    var reflowQueue = [];      $provide.value('$$animateReflow', function(fn) { +      var index = reflowQueue.length;        reflowQueue.push(fn); -      return angular.noop; +      return function cancel() { +        reflowQueue.splice(index, 1); +      };      }); -    $provide.decorator('$animate', function($delegate) { +    $provide.decorator('$animate', function($delegate, $$asyncCallback) {        var animate = {          queue : [],          enabled : $delegate.enabled, +        triggerCallbacks : function() { +          $$asyncCallback.flush(); +        },          triggerReflow : function() { -          if(reflowQueue.length === 0) { -            throw new Error('No animation reflows present'); -          }            angular.forEach(reflowQueue, function(fn) {              fn();            }); diff --git a/test/ng/directive/ngClassSpec.js b/test/ng/directive/ngClassSpec.js index b11c4766..83cd3000 100644 --- a/test/ng/directive/ngClassSpec.js +++ b/test/ng/directive/ngClassSpec.js @@ -345,6 +345,7 @@ describe('ngClass animations', function() {      //mocks are not used since the enter delegation method is called before addClass and      //it makes it impossible to test to see that addClass is called first      module('ngAnimate'); +    module('ngAnimateMock');      var digestQueue = [];      module(function($animateProvider) { @@ -367,7 +368,7 @@ describe('ngClass animations', function() {          };        };      }); -    inject(function($compile, $rootScope, $rootElement, $animate, $timeout, $document) { +    inject(function($compile, $rootScope, $browser, $rootElement, $animate, $timeout, $document) {        // Enable animations by triggering the first item in the postDigest queue        digestQueue.shift()(); @@ -407,7 +408,7 @@ describe('ngClass animations', function() {        //is spaced-out then it is required so that the original digestion        //is kicked into gear        $rootScope.$digest(); -      $timeout.flush(); +      $animate.triggerCallbacks();        expect(element.data('state')).toBe('crazy-enter');        expect(enterComplete).toBe(true); diff --git a/test/ng/directive/ngIncludeSpec.js b/test/ng/directive/ngIncludeSpec.js index ebb35147..9f37d1fe 100644 --- a/test/ng/directive/ngIncludeSpec.js +++ b/test/ng/directive/ngIncludeSpec.js @@ -368,7 +368,7 @@ describe('ngInclude', function() {        expect(autoScrollSpy).not.toHaveBeenCalled();        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        expect(autoScrollSpy).toHaveBeenCalledOnce();      })); @@ -385,7 +385,7 @@ describe('ngInclude', function() {        });        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        $rootScope.$apply(function () {          $rootScope.tpl = 'another.html'; @@ -394,7 +394,7 @@ describe('ngInclude', function() {        expect($animate.queue.shift().event).toBe('leave');        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        $rootScope.$apply(function() {          $rootScope.tpl = 'template.html'; @@ -403,7 +403,7 @@ describe('ngInclude', function() {        expect($animate.queue.shift().event).toBe('leave');        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        expect(autoScrollSpy).toHaveBeenCalled();        expect(autoScrollSpy.callCount).toBe(3); @@ -419,7 +419,7 @@ describe('ngInclude', function() {        });        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        expect(autoScrollSpy).not.toHaveBeenCalled();      })); @@ -435,7 +435,7 @@ describe('ngInclude', function() {        });        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        $rootScope.$apply(function () {          $rootScope.tpl = 'template.html'; @@ -457,7 +457,7 @@ describe('ngInclude', function() {            $rootScope.$apply("tpl = 'template.html'");            expect($animate.queue.shift().event).toBe('enter'); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(autoScrollSpy).toHaveBeenCalledOnce();      })); diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index b732d7b5..47e7afff 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -380,7 +380,7 @@ describe("ngAnimate", function() {            expect(child.attr('class')).toContain('ng-enter');            expect(child.attr('class')).toContain('ng-enter-active');            browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 }); -          $timeout.flush(); +          $animate.triggerCallbacks();            //move            element.append(after); @@ -391,7 +391,7 @@ describe("ngAnimate", function() {            expect(child.attr('class')).toContain('ng-move');            expect(child.attr('class')).toContain('ng-move-active');            browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 }); -          $timeout.flush(); +          $animate.triggerCallbacks();            //hide            $animate.addClass(child, 'ng-hide'); @@ -537,7 +537,7 @@ describe("ngAnimate", function() {              $animate.triggerReflow();              browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });            } -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(completed).toBe(true);          })); @@ -782,7 +782,7 @@ describe("ngAnimate", function() {              $animate.enabled(true); -            ss.addRule('.real-animation.ng-enter, .real-animation.ng-leave, .real-animation-fake.ng-enter, .real-animation-fake.ng-leave', +            ss.addRule('.real-animation.ng-enter, .real-animation.ng-leave',                '-webkit-animation:1s my_animation;' +                'animation:1s my_animation;'); @@ -817,6 +817,9 @@ describe("ngAnimate", function() {              expect(elements[3].attr('style')).toMatch(/animation-delay: 0\.3\d*s/);              expect(elements[4].attr('style')).toMatch(/animation-delay: 0\.4\d*s/); +            //final closing timeout +            $timeout.flush(); +              for(var i = 0; i < 5; i++) {                dealoc(elements[i]);                var newScope = $rootScope.$new(); @@ -826,13 +829,9 @@ describe("ngAnimate", function() {              };              $rootScope.$digest(); -            var expectFailure = true; -            try { -              $animate.triggerReflow(); -              expectFailure = false; -            } catch(e) {} -            expect(expectFailure).toBe(true); +            //this means no animations were triggered +            $timeout.verifyNoPendingTasks();              expect(elements[0].attr('style')).toBeFalsy();              expect(elements[1].attr('style')).not.toMatch(/animation-delay: 0\.1\d*s/); @@ -1117,14 +1116,7 @@ describe("ngAnimate", function() {              };              $rootScope.$digest(); - -            var expectFailure = true; -            try { -              $animate.triggerReflow(); -              expectFailure = false; -            } catch(e) {} - -            expect(expectFailure).toBe(true); +            $animate.triggerReflow();              expect(elements[0].attr('style')).toBeFalsy();              expect(elements[1].attr('style')).not.toMatch(/transition-delay: 0\.1\d*s/); @@ -1337,7 +1329,7 @@ describe("ngAnimate", function() {              expect(element.hasClass('ng-enter')).toBe(true);              expect(element.hasClass('ng-enter-active')).toBe(true);              browserTrigger(element,'transitionend', { timeStamp: Date.now() + 22000, elapsedTime: 22 }); -            $timeout.flush(); +            $animate.triggerCallbacks();            }            expect(element.hasClass('abc')).toBe(true); @@ -1351,7 +1343,7 @@ describe("ngAnimate", function() {              expect(element.hasClass('ng-enter')).toBe(true);              expect(element.hasClass('ng-enter-active')).toBe(true);              browserTrigger(element,'transitionend', { timeStamp: Date.now() + 11000, elapsedTime: 11 }); -            $timeout.flush(); +            $animate.triggerCallbacks();            }            expect(element.hasClass('xyz')).toBe(true);          })); @@ -1426,7 +1418,7 @@ describe("ngAnimate", function() {            });            $rootScope.$digest(); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(flag).toBe(true);          })); @@ -1446,7 +1438,7 @@ describe("ngAnimate", function() {            });            $rootScope.$digest(); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(flag).toBe(true);          })); @@ -1467,7 +1459,7 @@ describe("ngAnimate", function() {            });            $rootScope.$digest(); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(flag).toBe(true);            expect(element.parent().id).toBe(parent2.id); @@ -1493,7 +1485,7 @@ describe("ngAnimate", function() {              signature += 'B';            }); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(signature).toBe('AB');          })); @@ -1529,17 +1521,19 @@ describe("ngAnimate", function() {              steps.push(['done', 'klass', 'addClass']);            }); -          $timeout.flush(1); +          $animate.triggerCallbacks();            expect(steps.pop()).toEqual(['before', 'klass', 'addClass']);            $animate.triggerReflow(); -          $timeout.flush(1); + +          $animate.triggerCallbacks();            expect(steps.pop()).toEqual(['after', 'klass', 'addClass']);            browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 }); -          $timeout.flush(1); + +          $animate.triggerCallbacks();            expect(steps.shift()).toEqual(['close', 'klass', 'addClass']); @@ -1568,7 +1562,7 @@ describe("ngAnimate", function() {            $animate.enter(element, parent);            $rootScope.$digest(); -          $timeout.flush(1); +          $animate.triggerCallbacks();            expect(steps.shift()).toEqual(['before', 'ng-enter', 'enter']);            expect(steps.shift()).toEqual(['after',  'ng-enter', 'enter']); @@ -1602,7 +1596,7 @@ describe("ngAnimate", function() {              flag = true;            }); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(flag).toBe(true);          })); @@ -1629,7 +1623,7 @@ describe("ngAnimate", function() {              $animate.triggerReflow();              browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });            } -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(flag).toBe(true);          })); @@ -1648,7 +1642,7 @@ describe("ngAnimate", function() {              flag = true;            }); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(flag).toBe(true);          })); @@ -1677,8 +1671,9 @@ describe("ngAnimate", function() {            $animate.addClass(element, 'ng-hide'); //earlier animation cancelled            if($sniffer.transitions) {              $animate.triggerReflow(); +            browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 9 });            } -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(signature).toBe('AB');          }));        }); @@ -1707,7 +1702,7 @@ describe("ngAnimate", function() {          it("should not perform an animation, and the followup DOM operation, if the class is " +             "already present during addClass or not present during removeClass on the element", -          inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) { +          inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout, $browser) {            var element = jqLite('<div class="klassy"></div>');            $rootElement.append(element); @@ -1781,7 +1776,7 @@ describe("ngAnimate", function() {              signature += 'B';            }); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(element.hasClass('klass')).toBe(false);            expect(signature).toBe('AB');          })); @@ -1813,7 +1808,8 @@ describe("ngAnimate", function() {              expect(element.hasClass('klass-add-active')).toBe(true);              browserTrigger(element,'transitionend', { timeStamp: Date.now() + 3000, elapsedTime: 3 });            } -          $timeout.flush(); + +          $animate.triggerCallbacks();            //this cancels out the older animation            $animate.removeClass(element,'klass', function() { @@ -1830,7 +1826,8 @@ describe("ngAnimate", function() {              browserTrigger(element,'transitionend', { timeStamp: Date.now() + 3000, elapsedTime: 3 });            } -          $timeout.flush(); + +          $animate.triggerCallbacks();            expect(element.hasClass('klass')).toBe(false);            expect(signature).toBe('12'); @@ -1863,6 +1860,7 @@ describe("ngAnimate", function() {            expect(element.hasClass('klassy')).toBe(false); +          $animate.triggerCallbacks();            expect(signature).toBe('XY');          })); @@ -1895,7 +1893,7 @@ describe("ngAnimate", function() {              expect(element.hasClass('klass-add-active')).toBe(false);            } -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(element.hasClass('klass')).toBe(true);            $animate.removeClass(element,'klass', function() { @@ -1911,7 +1909,7 @@ describe("ngAnimate", function() {              expect(element.hasClass('klass-remove-active')).toBe(false);            } -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(element.hasClass('klass')).toBe(false);            expect(signature).toBe('db'); @@ -1951,7 +1949,7 @@ describe("ngAnimate", function() {              expect(element.hasClass('two-add-active')).toBe(false);            } -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(element.hasClass('one')).toBe(true);            expect(element.hasClass('two')).toBe(true); @@ -1997,7 +1995,7 @@ describe("ngAnimate", function() {              expect(element.hasClass('two-remove-active')).toBe(false);            } -          $timeout.flush(); +          $animate.triggerCallbacks();            expect(element.hasClass('one')).toBe(false);            expect(element.hasClass('two')).toBe(false); @@ -2159,7 +2157,7 @@ describe("ngAnimate", function() {          child.addClass('usurper');          $animate.leave(child);          $rootScope.$digest(); -        $timeout.flush(); +        $animate.triggerCallbacks();          expect(child.hasClass('ng-enter')).toBe(false);          expect(child.hasClass('ng-enter-active')).toBe(false); @@ -2213,10 +2211,10 @@ describe("ngAnimate", function() {    //    if($sniffer.transitions) {    //      expect(element.hasClass('on')).toBe(false);    //      expect(element.hasClass('on-add')).toBe(true); -  //      $timeout.flush(); +  //      $animate.triggerCallbacks();    //    }    // -  //    $timeout.flush(); +  //    $animate.triggerCallbacks();    //    //    expect(element.hasClass('on')).toBe(true);    //    expect(element.hasClass('on-add')).toBe(false); @@ -2229,7 +2227,7 @@ describe("ngAnimate", function() {    //      $timeout.flush(10000);    //    }    // -  //    $timeout.flush(); +  //    $animate.triggerCallbacks();    //    expect(element.hasClass('on')).toBe(false);    //    expect(element.hasClass('on-remove')).toBe(false);    //    expect(element.hasClass('on-remove-active')).toBe(false); @@ -2272,11 +2270,11 @@ describe("ngAnimate", function() {    //    //      if($sniffer.transitions) {    //        expect(element).toBeShown(); //still showing -  //        $timeout.flush(); +  //        $animate.triggerCallbacks();    //        expect(element).toBeShown();    //        $timeout.flush(5555);    //      } -  //      $timeout.flush(); +  //      $animate.triggerCallbacks();    //      expect(element).toBeHidden();    //    //      expect(element.hasClass('showing')).toBe(false); @@ -2285,11 +2283,11 @@ describe("ngAnimate", function() {    //    //      if($sniffer.transitions) {    //        expect(element).toBeHidden(); -  //        $timeout.flush(); +  //        $animate.triggerCallbacks();    //        expect(element).toBeHidden();    //        $timeout.flush(5580);    //      } -  //      $timeout.flush(); +  //      $animate.triggerCallbacks();    //      expect(element).toBeShown();    //    //      expect(element.hasClass('showing')).toBe(true); @@ -2484,7 +2482,7 @@ describe("ngAnimate", function() {          expect(animationState).toBe('enter-cancel');          $rootScope.$digest(); -        $timeout.flush(); +        $animate.triggerCallbacks();          $animate.addClass(child, 'something');          if($sniffer.transitions) { @@ -2646,16 +2644,16 @@ describe("ngAnimate", function() {          expect(intercepted).toBe('move'); -        //flush the enter reflow -        $timeout.flush(); +        //flush the POST enter callback +        $animate.triggerCallbacks();          $animate.addClass(child2, 'testing');          expect(intercepted).toBe('move');          continueAnimation(); -        //flush the move reflow -        $timeout.flush(); +        //flush the POST move callback +        $animate.triggerCallbacks();          $animate.leave(child2);          $rootScope.$digest(); @@ -3100,7 +3098,7 @@ describe("ngAnimate", function() {          forEach(element.children(), function(kid) {            browserTrigger(kid, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });          }); -        $timeout.flush(); +        $animate.triggerCallbacks();          $rootScope.items = [];          $rootScope.$digest(); @@ -3141,7 +3139,7 @@ describe("ngAnimate", function() {          });          $rootScope.$digest(); -        $timeout.flush(); +        $animate.triggerCallbacks();          expect(captures['enter']).toBeUndefined();          expect(enterDone).toBe(true); @@ -3152,8 +3150,9 @@ describe("ngAnimate", function() {          $animate.leave(element, function() {            leaveDone = true;          }); +          $rootScope.$digest(); -        $timeout.flush(); +        $animate.triggerCallbacks();          expect(captures['leave']).toBe(true);          expect(leaveDone).toBe(true); @@ -3184,11 +3183,11 @@ describe("ngAnimate", function() {        $animate.triggerReflow();        browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 }); -      $timeout.flush(1);        expect(ready).toBe(false);        browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 5 }); -      $timeout.flush(1); +      $animate.triggerReflow(); +      $animate.triggerCallbacks();        expect(ready).toBe(true);        ready = false; @@ -3198,7 +3197,7 @@ describe("ngAnimate", function() {        $animate.triggerReflow();        browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 }); -      $timeout.flush(1); +      $animate.triggerCallbacks();        expect(ready).toBe(true);      })); @@ -3221,7 +3220,7 @@ describe("ngAnimate", function() {          ready = true;        }); -      $timeout.flush(1); +      $animate.triggerCallbacks();        expect(ready).toBe(true);      })); @@ -3248,14 +3247,42 @@ describe("ngAnimate", function() {        });        $animate.triggerReflow(); - -      $timeout.flush(1); +      $animate.triggerCallbacks();        expect(signature).toBe('A');        browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2000 }); -      $timeout.flush(1); +      $animate.triggerCallbacks();        expect(signature).toBe('AB');      })); + +    it('should cancel the previous reflow when new animations are added', function() { +      var cancelReflowCallback = jasmine.createSpy('callback'); +      module(function($provide) { +        $provide.value('$$animateReflow', function(fn) { +          return cancelReflowCallback; +        }); +      }); +      inject(function($animate, $sniffer, $rootScope, $compile) { +        if (!$sniffer.transitions) return; + +        ss.addRule('.fly', '-webkit-transition:2s linear all;' + +                                   'transition:2s linear all;'); + +        $animate.enabled(true); + +        var element = $compile('<div class="fly"></div>')($rootScope); +        $rootElement.append(element); +        jqLite($document[0].body).append($rootElement); + +        expect(cancelReflowCallback).not.toHaveBeenCalled(); + +        $animate.addClass(element, 'fast'); +        $animate.addClass(element, 'smooth'); +        $animate.triggerReflow(); + +        expect(cancelReflowCallback).toHaveBeenCalled(); +      }); +    });    });  }); diff --git a/test/ngRoute/directive/ngViewSpec.js b/test/ngRoute/directive/ngViewSpec.js index 072c232b..259940c6 100644 --- a/test/ngRoute/directive/ngViewSpec.js +++ b/test/ngRoute/directive/ngViewSpec.js @@ -707,7 +707,7 @@ describe('ngView animations', function() {        $location.path('/foo');        $rootScope.$digest(); -      $timeout.flush(); +      $animate.triggerCallbacks();        $location.path('/');        $rootScope.$digest(); @@ -872,7 +872,7 @@ describe('ngView animations', function() {        $location.path('/foo');        $rootScope.$digest();        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        expect(autoScrollSpy).toHaveBeenCalledOnce();      })); @@ -886,7 +886,7 @@ describe('ngView animations', function() {        $location.path('/foo');        $rootScope.$digest();        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        expect(autoScrollSpy).toHaveBeenCalledOnce();      })); @@ -899,7 +899,7 @@ describe('ngView animations', function() {        $location.path('/foo');        $rootScope.$digest();        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        expect(autoScrollSpy).not.toHaveBeenCalled();      })); @@ -913,7 +913,7 @@ describe('ngView animations', function() {        $location.path('/foo');        $rootScope.$digest();        expect($animate.queue.shift().event).toBe('enter'); -      $timeout.flush(); +      $animate.triggerCallbacks();        expect(autoScrollSpy).not.toHaveBeenCalled();      })); @@ -930,7 +930,7 @@ describe('ngView animations', function() {            expect(autoScrollSpy).not.toHaveBeenCalled();            expect($animate.queue.shift().event).toBe('enter'); -          $timeout.flush(); +          $animate.triggerCallbacks();            expect($animate.enter).toHaveBeenCalledOnce();            expect(autoScrollSpy).toHaveBeenCalledOnce(); | 
