aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatias Niemelä2014-02-24 19:43:57 -0500
committerMatias Niemelä2014-02-24 21:23:18 -0500
commit4c4537e65e6cf911c9659b562d89e3330ce3ffae (patch)
tree3f9e6736a314073b5516beab3c86d7709e887b6b
parent62761428eff3a53e69367449eb81869e59e75e39 (diff)
downloadangular.js-4c4537e65e6cf911c9659b562d89e3330ce3ffae.tar.bz2
perf($animate): use rAF instead of timeouts to issue animation callbacks
-rw-r--r--src/ng/animate.js16
-rw-r--r--src/ngAnimate/animate.js54
-rw-r--r--src/ngMock/angular-mocks.js15
-rw-r--r--test/ng/directive/ngClassSpec.js5
-rw-r--r--test/ng/directive/ngIncludeSpec.js14
-rw-r--r--test/ngAnimate/animateSpec.js153
-rw-r--r--test/ngRoute/directive/ngViewSpec.js12
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();