From 9e2cee27e278f0cdbe959df2d39cb393ae8e7ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Mon, 18 Nov 2013 16:20:22 -0500 Subject: [PATCH] fix($animate): ensure transition animations are unblocked before the dom operation occurs Transitions are blocked when the base CSS class is added at the start of the animation. This causes an issue if the followup CSS class contains animatable-styles. Now, once the animation active state is triggered (when the animation CSS dom operation occurs) the animation itself will always trigger an animate without a quick jump. Closes #5014 Closes #4265 --- src/ngAnimate/animate.js | 26 +++++++++++++++++++++----- test/ngAnimate/animateSpec.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index ab7c58164bd2..1956d54c7837 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -1001,7 +1001,7 @@ angular.module('ngAnimate', ['ng']) if(timings.transitionDuration > 0) { element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME); activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' '; - node.style[TRANSITION_PROP + PROPERTY_KEY] = 'none'; + blockTransitions(element); } forEach(className.split(' '), function(klass, i) { @@ -1021,6 +1021,17 @@ angular.module('ngAnimate', ['ng']) return true; } + function blockTransitions(element) { + element[0].style[TRANSITION_PROP + PROPERTY_KEY] = 'none'; + } + + function unblockTransitions(element) { + var node = element[0], prop = TRANSITION_PROP + PROPERTY_KEY; + if(node.style[prop] && node.style[prop].length > 0) { + node.style[prop] = ''; + } + } + function animateRun(element, className, activeAnimationComplete) { var data = element.data(NG_ANIMATE_CSS_DATA_KEY); if(!element.hasClass(className) || !data) { @@ -1041,8 +1052,6 @@ angular.module('ngAnimate', ['ng']) var applyFallbackStyle, style = ''; if(timings.transitionDuration > 0) { - node.style[TRANSITION_PROP + PROPERTY_KEY] = ''; - var propertyStyle = timings.transitionPropertyStyle; if(propertyStyle.indexOf('all') == -1) { applyFallbackStyle = true; @@ -1150,6 +1159,7 @@ angular.module('ngAnimate', ['ng']) //happen in the first place var cancel = preReflowCancellation; afterReflow(function() { + unblockTransitions(element); //once the reflow is complete then we point cancel to //the new cancellation function which will remove all of the //animation properties from the active animation @@ -1213,7 +1223,10 @@ angular.module('ngAnimate', ['ng']) beforeAddClass : function(element, className, animationCompleted) { var cancellationMethod = animateBefore(element, suffixClasses(className, '-add')); if(cancellationMethod) { - afterReflow(animationCompleted); + afterReflow(function() { + unblockTransitions(element); + animationCompleted(); + }); return cancellationMethod; } animationCompleted(); @@ -1226,7 +1239,10 @@ angular.module('ngAnimate', ['ng']) beforeRemoveClass : function(element, className, animationCompleted) { var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove')); if(cancellationMethod) { - afterReflow(animationCompleted); + afterReflow(function() { + unblockTransitions(element); + animationCompleted(); + }); return cancellationMethod; } animationCompleted(); diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index b9b7f9ed0eb8..2362576a5753 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -2663,4 +2663,37 @@ describe("ngAnimate", function() { expect(element.hasClass('base-class')).toBe(true); })); + it('should block and unblock transitions before the dom operation occurs', + inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer, $timeout) { + + if (!$sniffer.transitions) return; + + $animate.enabled(true); + + ss.addRule('.cross-animation', '-webkit-transition:1s linear all;' + + 'transition:1s linear all;'); + + var capturedProperty = 'none'; + + var element = $compile('
')($rootScope); + $rootElement.append(element); + jqLite($document[0].body).append($rootElement); + + var node = element[0]; + node._setAttribute = node.setAttribute; + node.setAttribute = function(prop, val) { + if(prop == 'class' && val.indexOf('trigger-class') >= 0) { + var propertyKey = ($sniffer.vendorPrefix == 'Webkit' ? '-webkit-' : '') + 'transition-property'; + capturedProperty = element.css(propertyKey); + } + node._setAttribute(prop, val); + }; + + $animate.addClass(element, 'trigger-class'); + + $timeout.flush(); + + expect(capturedProperty).not.toBe('none'); + })); + });