aboutsummaryrefslogtreecommitdiffstats
path: root/src/ngAnimate/animate.js
diff options
context:
space:
mode:
authorMatias Niemelä2013-10-17 22:37:17 -0400
committerMatias Niemelä2013-10-23 07:08:03 -0400
commitf5289fe84ffc1f2368dae7bd14c420abbe76749e (patch)
tree17e5d6a175b05ba6cde93f7ce709de3c3011768e /src/ngAnimate/animate.js
parent74912802c644ca929e39a7583cb7a9a05f12e91f (diff)
downloadangular.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/animate.js')
-rw-r--r--src/ngAnimate/animate.js86
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);
},