From 6760d7a315d7ea5cbd4f8ab74b200f754a2041f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Tue, 19 Nov 2013 00:21:48 -0500 Subject: [PATCH] fix($animate): ensure keyframe animations are blocked around the reflow Keyframe animations trigger on the first CSS class and not the second. This may cause a slight flicker during a stagger animation since the animation has already started before the stagger delay is considered. This fix ensures that the animation is blocked until the active animation starts which allows for staggering animations to take over properly. Closes #5018 --- src/ngAnimate/animate.js | 12 ++++++++++++ test/ngAnimate/animateSpec.js | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index 1956d54c7837..4d3a587831e4 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -1002,6 +1002,8 @@ angular.module('ngAnimate', ['ng']) element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME); activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' '; blockTransitions(element); + } else { + blockKeyframeAnimations(element); } forEach(className.split(' '), function(klass, i) { @@ -1025,6 +1027,10 @@ angular.module('ngAnimate', ['ng']) element[0].style[TRANSITION_PROP + PROPERTY_KEY] = 'none'; } + function blockKeyframeAnimations(element) { + element[0].style[ANIMATION_PROP] = 'none 0s'; + } + function unblockTransitions(element) { var node = element[0], prop = TRANSITION_PROP + PROPERTY_KEY; if(node.style[prop] && node.style[prop].length > 0) { @@ -1032,6 +1038,10 @@ angular.module('ngAnimate', ['ng']) } } + function unblockKeyframeAnimations(element) { + element[0].style[ANIMATION_PROP] = ''; + } + function animateRun(element, className, activeAnimationComplete) { var data = element.data(NG_ANIMATE_CSS_DATA_KEY); if(!element.hasClass(className) || !data) { @@ -1059,6 +1069,8 @@ angular.module('ngAnimate', ['ng']) style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; '; style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; '; } + } else { + unblockKeyframeAnimations(element); } if(ii > 0) { diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index 2362576a5753..cbf9de63a18d 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -2696,4 +2696,29 @@ describe("ngAnimate", function() { expect(capturedProperty).not.toBe('none'); })); + it('should block and unblock keyframe animations around the reflow operation', + inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer, $timeout) { + + if (!$sniffer.animations) return; + + $animate.enabled(true); + + ss.addRule('.cross-animation', '-webkit-animation:1s my_animation;' + + 'animation:1s my_animation;'); + + var element = $compile('
')($rootScope); + $rootElement.append(element); + jqLite($document[0].body).append($rootElement); + + var node = element[0]; + var animationKey = $sniffer.vendorPrefix == 'Webkit' ? 'WebkitAnimation' : 'animation'; + + $animate.addClass(element, 'trigger-class'); + + expect(node.style[animationKey]).toContain('none'); + + $timeout.flush(); + + expect(node.style[animationKey]).not.toContain('none'); + })); });