fix($animate): clear the GCS cache even when no animation is detected

$animate will cache subsequent calls to GCS in the event that the element
with the same CSS classes and the same parentNode is being animated. Once the
animation is started then $animate waits for one rAF before flushing the GCS
lookup cache. Prior to this fix, if GCS was unable to detect any transitions
or keyframes on the element then it would simply close the animation, but it
would not trigger the rAF code to flush the cache. This issue caused a bug
which made it difficult to detect why certain animations are not allowed to
fire if the element didn't contain any CSS-based animations beforehand.

Closes #8813
This commit is contained in:
Matias Niemelä
2014-10-02 12:41:36 +02:00
parent a75546afdf
commit cb85cbcec1
2 changed files with 72 additions and 0 deletions

View File

@@ -1419,6 +1419,16 @@ angular.module('ngAnimate', ['ng'])
var parentCounter = 0;
var animationReflowQueue = [];
var cancelAnimationReflow;
function clearCacheAfterReflow() {
if (!cancelAnimationReflow) {
cancelAnimationReflow = $$animateReflow(function() {
animationReflowQueue = [];
cancelAnimationReflow = null;
lookupCache = {};
});
}
}
function afterReflow(element, callback) {
if (cancelAnimationReflow) {
cancelAnimationReflow();
@@ -1764,6 +1774,7 @@ angular.module('ngAnimate', ['ng'])
//to perform at all
var preReflowCancellation = animateBefore(animationEvent, element, className);
if (!preReflowCancellation) {
clearCacheAfterReflow();
animationComplete();
return;
}
@@ -1820,6 +1831,7 @@ angular.module('ngAnimate', ['ng'])
afterReflow(element, animationCompleted);
return cancellationMethod;
}
clearCacheAfterReflow();
animationCompleted();
},
@@ -1829,6 +1841,7 @@ angular.module('ngAnimate', ['ng'])
afterReflow(element, animationCompleted);
return cancellationMethod;
}
clearCacheAfterReflow();
animationCompleted();
},
@@ -1838,6 +1851,7 @@ angular.module('ngAnimate', ['ng'])
afterReflow(element, animationCompleted);
return cancellationMethod;
}
clearCacheAfterReflow();
animationCompleted();
},

View File

@@ -3757,6 +3757,64 @@ describe("ngAnimate", function() {
expect(inner.hasClass('on-add-active')).toBe(false);
}));
it("should reset the getComputedStyle lookup cache even when no animation is found",
inject(function($compile, $rootScope, $animate, $sniffer, $document) {
if (!$sniffer.transitions) return;
$animate.enabled();
var html = '<div>' +
' <div class="toggle" ng-if="onOff">On or Off</div>' +
'</div>';
ss.addRule('.activated .toggle', '-webkit-transition:1s linear all;' +
'transition:1s linear all;');
var child, element = $compile(html)($rootScope);
$rootElement.append(element);
jqLite($document[0].body).append($rootElement);
$rootScope.onOff = true;
$rootScope.$digest();
child = element.find('div');
expect(child).not.toHaveClass('ng-enter');
expect(child.parent()[0]).toEqual(element[0]);
$animate.triggerReflow();
$rootScope.onOff = false;
$rootScope.$digest();
child = element.find('div');
expect(child.parent().length).toBe(0);
$animate.triggerReflow();
element.addClass('activated');
$rootScope.$digest();
$animate.triggerReflow();
$rootScope.onOff = true;
$rootScope.$digest();
child = element.find('div');
expect(child).toHaveClass('ng-enter');
$animate.triggerReflow();
expect(child).toHaveClass('ng-enter-active');
browserTrigger(child, 'transitionend',
{ timeStamp: Date.now() + 1000, elapsedTime: 2000 });
$animate.triggerCallbacks();
$rootScope.onOff = false;
$rootScope.$digest();
expect(child).toHaveClass('ng-leave');
$animate.triggerReflow();
expect(child).toHaveClass('ng-leave-active');
}));
it("should cancel and perform the dom operation only after the reflow has run",
inject(function($compile, $rootScope, $animate, $sniffer) {