From f10c79afe64346eb62035955f142ea614ff2ab1e Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Wed, 2 Nov 2016 18:06:25 +0100 Subject: [PATCH] fix($animateCss): avoid flicker caused by temporary classes --- src/ngAnimate/animateCss.js | 12 ++++++ src/ngAnimate/animation.js | 1 - test/ngAnimate/integrationSpec.js | 66 +++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/ngAnimate/animateCss.js b/src/ngAnimate/animateCss.js index 27619d67c29b..94024c2775cf 100644 --- a/src/ngAnimate/animateCss.js +++ b/src/ngAnimate/animateCss.js @@ -692,6 +692,13 @@ var $AnimateCssProvider = ['$animateProvider', /** @this */ function($animatePro runner = new $$AnimateRunner(runnerHost); + if (options.tempClasses) { + // Since animateCss waits for an animation frame to start the actual + // animation, the temp class styles might be visible for a very short time. + // Removing and re-adding before the actual start solves this problem. + $$jqLite.removeClass(element, options.tempClasses); + } + waitUntilQuiet(start); // we don't have access to pause/resume the animation @@ -835,6 +842,11 @@ var $AnimateCssProvider = ['$animateProvider', /** @this */ function($animatePro return; } + if (options.tempClasses) { + $$jqLite.addClass(element, options.tempClasses); + options.tempClasses = null; + } + // even though we only pause keyframe animations here the pause flag // will still happen when transitions are used. Only the transition will // not be paused since that is not possible. If the animation ends when diff --git a/src/ngAnimate/animation.js b/src/ngAnimate/animation.js index 51f104ed7cb8..a2c388f09975 100644 --- a/src/ngAnimate/animation.js +++ b/src/ngAnimate/animation.js @@ -134,7 +134,6 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro var tempClasses = options.tempClasses; if (tempClasses) { classes += ' ' + tempClasses; - options.tempClasses = null; } var prepareClassName; diff --git a/test/ngAnimate/integrationSpec.js b/test/ngAnimate/integrationSpec.js index c17b737c6e7f..e8852085432b 100644 --- a/test/ngAnimate/integrationSpec.js +++ b/test/ngAnimate/integrationSpec.js @@ -544,6 +544,72 @@ describe('ngAnimate integration tests', function() { expect(child).not.toHaveClass('blue'); }); }); + + it('should remove a temporary class when the animation starts, then re-add it when it is triggered', function() { + + var elementClassList = []; + + var hasTempClass = { + beforeCssAnimationStart: null, + afterCssAnimationStart: null, + afterCssAnimationTriggered: null, + afterCssAnimationFinished: null + }; + + module(function($provide) { + $provide.decorator('$animateCss', function($delegate) { + var decorated = function() { + var element = arguments[0]; + var animator = $delegate.apply(this, arguments); + var startFn = animator.start; + + animator.start = function() { + hasTempClass.beforeCssAnimationStart = element[0].classList.contains('temp-class'); + var runner = startFn.apply(this, arguments); + hasTempClass.afterCssAnimationStart = element[0].classList.contains('temp-class'); + return runner; + }; + + return animator; + }; + + return decorated; + }); + }); + + inject(function($animate, $compile, $rootScope, $rootElement, $$rAF) { + element = jqLite('
'); + $compile(element)($rootScope); + + var className = 'klass'; + var parent = jqLite('
'); + html(parent); + + parent.append(element); + + ss.addRule('.animate-me', 'transition:2s linear all;'); + + var runner = $animate.addClass(element, className, { + tempClasses: 'temp-class' + }); + + $rootScope.$digest(); + $animate.flush(); + + hasTempClass.afterCssAnimationTriggered = element[0].classList.contains('temp-class'); + + browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2 }); + $animate.flush(); + + $rootScope.$digest(); + hasTempClass.afterCssAnimationFinished = element[0].classList.contains('temp-class'); + + expect(hasTempClass.beforeCssAnimationStart).toBe(true); + expect(hasTempClass.afterCssAnimationStart).toBe(false); + expect(hasTempClass.afterCssAnimationTriggered).toBe(true); + expect(hasTempClass.afterCssAnimationFinished).toBe(false); + }); + }); }); describe('JS animations', function() {