Skip to content

Commit 9a1b370

Browse files
committed
fix($animate): ensure all animated elements are taken care of during the closing timeout
Closes angular#6395
1 parent 348a771 commit 9a1b370

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

src/ngAnimate/animate.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -1081,17 +1081,22 @@ angular.module('ngAnimate', ['ng'])
10811081
var closingTimestamp = 0;
10821082
var animationElementQueue = [];
10831083
function animationCloseHandler(element, totalTime) {
1084+
var node = extractElementNode(element);
1085+
element = angular.element(node);
1086+
1087+
//this item will be garbage collected by the closing
1088+
//animation timeout
1089+
animationElementQueue.push(element);
1090+
1091+
//but it may not need to cancel out the existing timeout
1092+
//if the timestamp is less than the previous one
10841093
var futureTimestamp = Date.now() + (totalTime * 1000);
10851094
if(futureTimestamp <= closingTimestamp) {
10861095
return;
10871096
}
10881097

10891098
$timeout.cancel(closingTimer);
10901099

1091-
var node = extractElementNode(element);
1092-
element = angular.element(node);
1093-
animationElementQueue.push(element);
1094-
10951100
closingTimestamp = futureTimestamp;
10961101
closingTimer = $timeout(function() {
10971102
closeAllAnimations(animationElementQueue);
@@ -1345,7 +1350,7 @@ angular.module('ngAnimate', ['ng'])
13451350

13461351
var staggerTime = itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
13471352
var animationTime = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER;
1348-
var totalTime = (staggerTime + animationTime) * ONE_SECOND;
1353+
var totalTime = staggerTime + animationTime * ONE_SECOND;
13491354

13501355
elementData.running++;
13511356
animationCloseHandler(element, totalTime);

test/ngAnimate/animateSpec.js

+48
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,54 @@ describe("ngAnimate", function() {
11891189
expect(element.hasClass('some-class-add-active')).toBe(false);
11901190
}));
11911191

1192+
it("should intelligently cancel former timeouts and close off a series of elements a final timeout", function() {
1193+
var cancellations = 0;
1194+
module(function($provide) {
1195+
//temporarily override this factory so that the cancellations
1196+
//are only counted for the the animations
1197+
//
1198+
// TODO(matsko): remove this line once rAF callbacks are merged
1199+
$provide.value('$$asyncQueueBuffer', angular.noop);
1200+
$provide.decorator('$timeout', function($delegate) {
1201+
var _cancel = $delegate.cancel;
1202+
$delegate.cancel = function() {
1203+
cancellations++;
1204+
return _cancel.apply($delegate, arguments);
1205+
};
1206+
return $delegate;
1207+
});
1208+
})
1209+
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
1210+
if (!$sniffer.transitions) return;
1211+
1212+
ss.addRule('.animate-me', '-webkit-transition:1s linear all;' +
1213+
'transition:1s linear all;');
1214+
1215+
element = $compile(html('<div><div class="animate-me" ng-repeat="item in items"></div></div>'))($rootScope);
1216+
1217+
$rootScope.items = [1,2,3,4,5,6,7,8,9,10];
1218+
var totalOperations = $rootScope.items.length;
1219+
1220+
$rootScope.$digest();
1221+
1222+
$rootScope.items = [0];
1223+
$animate.triggerReflow();
1224+
$timeout.flush(1500);
1225+
1226+
expect(cancellations).toBeLessThan(totalOperations);
1227+
expect(element.children().length).toBe(10);
1228+
cancellations = 0;
1229+
1230+
$rootScope.items = [1];
1231+
$rootScope.$digest();
1232+
1233+
$animate.triggerReflow();
1234+
$timeout.flush(1500);
1235+
expect(element.children().length).toBe(1);
1236+
expect(cancellations).toBeLessThan(totalOperations);
1237+
});
1238+
});
1239+
11921240
it("apply a closing timeout with respect to a staggering animation",
11931241
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
11941242

0 commit comments

Comments
 (0)