Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

fix($animate): ensure all animated elements are taken care of during the closing timeout #6430

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/ngAnimate/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -1063,17 +1063,22 @@ angular.module('ngAnimate', ['ng'])
var closingTimestamp = 0;
var animationElementQueue = [];
function animationCloseHandler(element, totalTime) {
var node = extractElementNode(element);
element = angular.element(node);

//this item will be garbage collected by the closing
//animation timeout
animationElementQueue.push(element);

//but it may not need to cancel out the existing timeout
//if the timestamp is less than the previous one
var futureTimestamp = Date.now() + (totalTime * 1000);
if(futureTimestamp <= closingTimestamp) {
return;
}

$timeout.cancel(closingTimer);

var node = extractElementNode(element);
element = angular.element(node);
animationElementQueue.push(element);

closingTimestamp = futureTimestamp;
closingTimer = $timeout(function() {
closeAllAnimations(animationElementQueue);
Expand Down
53 changes: 52 additions & 1 deletion test/ngAnimate/animateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,49 @@ describe("ngAnimate", function() {
expect(element.hasClass('some-class-add-active')).toBe(false);
}));

it("should intelligently cancel former timeouts and close off a series of elements a final timeout", function() {
var cancellations = 0;
module(function($provide) {
$provide.decorator('$timeout', function($delegate) {
var _cancel = $delegate.cancel;
$delegate.cancel = function() {
cancellations++;
return _cancel.apply($delegate, arguments);
};
return $delegate;
});
})
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
if (!$sniffer.transitions) return;

ss.addRule('.animate-me', '-webkit-transition:1s linear all;' +
'transition:1s linear all;');

element = $compile(html('<div><div class="animate-me" ng-repeat="item in items"></div></div>'))($rootScope);

$rootScope.items = [1,2,3,4,5,6,7,8,9,10];
var totalOperations = $rootScope.items.length;

$rootScope.$digest();

$rootScope.items = [0];
$animate.triggerReflow();
$timeout.flush(1500);

expect(cancellations).toBeLessThan(totalOperations);
expect(element.children().length).toBe(10);
cancellations = 0;

$rootScope.items = [1];
$rootScope.$digest();

$animate.triggerReflow();
$timeout.flush(1500);
expect(element.children().length).toBe(1);
expect(cancellations).toBeLessThan(totalOperations);
});
});

it("apply a closing timeout with respect to a staggering animation",
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {

Expand Down Expand Up @@ -1216,7 +1259,15 @@ describe("ngAnimate", function() {
}

//(stagger * index) + (duration + delay) * 150%
$timeout.flush(9500); //0.5 * 4 + 5 * 1.5 = 9500;
//0.5 * 4 + 5 * 1.5 = 9500;
//9500 - 7500 = 2000
$timeout.flush(1999); //remove 1999 more

for(var i = 0; i < 5; i++) {
expect(kids[i].hasClass('ng-enter-active')).toBe(true);
}

$timeout.flush(1); //up to 2000ms

for(var i = 0; i < 5; i++) {
expect(kids[i].hasClass('ng-enter-active')).toBe(false);
Expand Down