diff options
| author | Matias Niemelä | 2013-10-17 22:37:17 -0400 | 
|---|---|---|
| committer | Matias Niemelä | 2013-10-23 07:08:03 -0400 | 
| commit | f5289fe84ffc1f2368dae7bd14c420abbe76749e (patch) | |
| tree | 17e5d6a175b05ba6cde93f7ce709de3c3011768e /src/ngAnimate | |
| parent | 74912802c644ca929e39a7583cb7a9a05f12e91f (diff) | |
| download | angular.js-f5289fe84ffc1f2368dae7bd14c420abbe76749e.tar.bz2 | |
fix($animate): only cancel class-based animations if the follow-up class contains CSS transition/keyframe animation code
Closes #4463
Closes #3784
Diffstat (limited to 'src/ngAnimate')
| -rw-r--r-- | src/ngAnimate/animate.js | 86 | 
1 files changed, 66 insertions, 20 deletions
diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index 20b9101e..f1945d02 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -493,35 +493,47 @@ angular.module('ngAnimate', ['ng'])        */        function performAnimation(event, className, element, parent, after, onComplete) {          var classes = (element.attr('class') || '') + ' ' + className; -        var animationLookup = (' ' + classes).replace(/\s+/g,'.'), -            animations = []; -        forEach(lookup(animationLookup), function(animation, index) { -          animations.push({ -            start : animation[event] -          }); -        }); - +        var animationLookup = (' ' + classes).replace(/\s+/g,'.');          if (!parent) {            parent = after ? after.parent() : element.parent();          } +          var disabledAnimation = { running : true }; +        var matches = lookup(animationLookup); +        var isClassBased = event == 'addClass' || event == 'removeClass'; +        var ngAnimateState = element.data(NG_ANIMATE_STATE) || {}; -        //skip the animation if animations are disabled, a parent is already being animated -        //or the element is not currently attached to the document body. -        if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running || animations.length === 0) { +        //skip the animation if animations are disabled, a parent is already being animated, +        //the element is not currently attached to the document body or then completely close +        //the animation if any matching animations are not found at all. +        //NOTE: IE8 + IE9 should close properly (run done()) in case a NO animation is not found. +        if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running || matches.length == 0) {            done();            return;          } -        var ngAnimateState = element.data(NG_ANIMATE_STATE) || {}; +        var animations = []; +        //only add animations if the currently running animation is not structural +        //or if there is no animation running at all +        if(!ngAnimateState.running || !(isClassBased && ngAnimateState.structural)) { +          forEach(matches, function(animation) { +            //add the animation to the queue to if it is allowed to be cancelled +            if(!animation.allowCancel || animation.allowCancel(element, event, className)) { +              animations.push({ +                start : animation[event] +              }); +            } +          }); +        } -        var isClassBased = event == 'addClass' || event == 'removeClass'; -        if(ngAnimateState.running) { -          if(isClassBased && ngAnimateState.structural) { -            onComplete && onComplete(); -            return; -          } +        //this would mean that an animation was not allowed so let the existing +        //animation do it's thing and close this one early +        if(animations.length == 0) { +          onComplete && onComplete(); +          return; +        } +        if(ngAnimateState.running) {            //if an animation is currently running on the element then lets take the steps            //to cancel that animation and fire any required callbacks            $timeout.cancel(ngAnimateState.flagTimer); @@ -651,6 +663,7 @@ angular.module('ngAnimate', ['ng'])            animationIterationCountKey = 'IterationCount';        var NG_ANIMATE_PARENT_KEY = '$ngAnimateKey'; +      var NG_ANIMATE_CLASS_KEY = '$$ngAnimateClasses';        var lookupCache = {};        var parentCounter = 0; @@ -669,7 +682,7 @@ angular.module('ngAnimate', ['ng'])        }        function getElementAnimationDetails(element, cacheKey, onlyCheckTransition) { -        var data = lookupCache[cacheKey]; +        var data = cacheKey ? lookupCache[cacheKey] : null;          if(!data) {            var transitionDuration = 0, transitionDelay = 0,                animationDuration = 0, animationDelay = 0; @@ -702,7 +715,9 @@ angular.module('ngAnimate', ['ng'])              transitionDuration : transitionDuration,              animationDuration : animationDuration            }; -          lookupCache[cacheKey] = data; +          if(cacheKey) { +            lookupCache[cacheKey] = data; +          }          }          return data;        } @@ -769,6 +784,7 @@ angular.module('ngAnimate', ['ng'])              element.addClass(activeClassName);            }); +          element.data(NG_ANIMATE_CLASS_KEY, className + ' ' + activeClassName);            element.on(css3AnimationEvents, onAnimationProgress);            // This will automatically be called by $animate so @@ -778,6 +794,7 @@ angular.module('ngAnimate', ['ng'])              element.off(css3AnimationEvents, onAnimationProgress);              element.removeClass(className);              element.removeClass(activeClassName); +            element.removeData(NG_ANIMATE_CLASS_KEY);              // Only when the animation is cancelled is the done()              // function not called for this animation therefore @@ -811,6 +828,35 @@ angular.module('ngAnimate', ['ng'])        }        return { +        allowCancel : function(element, event, className) { +          //always cancel the current animation if it is a +          //structural animation +          var oldClasses = element.data(NG_ANIMATE_CLASS_KEY); +          if(!oldClasses || ['enter','leave','move'].indexOf(event) >= 0) { +            return true; +          } + +          var parent = element.parent(); +          var clone = angular.element(element[0].cloneNode()); + +          //make the element super hidden and override any CSS style values +          clone.attr('style','position:absolute; top:-9999px; left:-9999px'); +          clone.removeAttr('id'); +          clone.html(''); + +          angular.forEach(oldClasses.split(' '), function(klass) { +            clone.removeClass(klass); +          }); + +          var suffix = event == 'addClass' ? '-add' : '-remove'; +          clone.addClass(suffixClasses(className, suffix)); +          parent.append(clone); + +          var timings = getElementAnimationDetails(clone); +          clone.remove(); + +          return Math.max(timings.transitionDuration, timings.animationDuration) > 0; +        },          enter : function(element, done) {            return animate(element, 'ng-enter', done);          },  | 
