Skip to content

Commit ff19137

Browse files
committedJan 5, 2016
fix(ngAnimate): allow removing classes that are added by a running animation
This allows follow-up animations to remove a class that is currently being added. Fixes angular#13339 Fixes angular#13380 Closes angular#13414
1 parent 0e03644 commit ff19137

File tree

2 files changed

+79
-4
lines changed

2 files changed

+79
-4
lines changed
 

‎src/ngAnimate/animateQueue.js

+40-4
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,36 @@ var NG_ANIMATE_PIN_DATA = '$ngAnimatePin';
55
var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
66
var PRE_DIGEST_STATE = 1;
77
var RUNNING_STATE = 2;
8+
var ONE_SPACE = ' ';
89

910
var rules = this.rules = {
1011
skip: [],
1112
cancel: [],
1213
join: []
1314
};
1415

16+
function makeTruthyCssClassMap(classString) {
17+
if (!classString) {
18+
return null;
19+
}
20+
21+
var keys = classString.split(ONE_SPACE);
22+
var map = Object.create(null);
23+
24+
forEach(keys, function(key) {
25+
map[key] = true;
26+
});
27+
return map;
28+
}
29+
30+
function hasMatchingClasses(classString, classMap) {
31+
if (classString && classMap) {
32+
return classString.split(ONE_SPACE).some(function(className) {
33+
return classMap[className];
34+
});
35+
}
36+
}
37+
1538
function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
1639
return rules[ruleType].some(function(fn) {
1740
return fn(element, currentAnimation, previousAnimation);
@@ -59,11 +82,24 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
5982
});
6083

6184
rules.cancel.push(function(element, newAnimation, currentAnimation) {
62-
var nO = newAnimation.options;
63-
var cO = currentAnimation.options;
6485

65-
// if the exact same CSS class is added/removed then it's safe to cancel it
66-
return (nO.addClass && nO.addClass === cO.removeClass) || (nO.removeClass && nO.removeClass === cO.addClass);
86+
87+
var nA = newAnimation.options.addClass;
88+
var nR = newAnimation.options.removeClass;
89+
var cA = currentAnimation.options.addClass;
90+
var cR = currentAnimation.options.removeClass;
91+
92+
// early detection to save the global CPU shortage :)
93+
if ((!isDefined(nA) && !isDefined(nR)) || (!isDefined(cA) && !isDefined(cR))) {
94+
return false;
95+
}
96+
97+
var cancelSomething = false;
98+
99+
cA = makeTruthyCssClassMap(cA);
100+
cR = makeTruthyCssClassMap(cR);
101+
102+
return (hasMatchingClasses(nA, cR)) || (hasMatchingClasses(nR, cA));
67103
});
68104

69105
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',

‎test/ngAnimate/integrationSpec.js

+39
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,45 @@ describe('ngAnimate integration tests', function() {
387387

388388
dealoc(element);
389389
}));
390+
391+
392+
it("should remove a class when the same class is currently being added by a joined class-based animation",
393+
inject(function($animate, $animateCss, $rootScope, $document, $rootElement, $$rAF) {
394+
395+
ss.addRule('.hide', 'opacity: 0');
396+
ss.addRule('.hide-add, .hide-remove', 'transition: 1s linear all');
397+
398+
jqLite($document[0].body).append($rootElement);
399+
element = jqLite('<div></div>');
400+
$rootElement.append(element);
401+
402+
// These animations will be joined together
403+
$animate.addClass(element, 'red');
404+
$animate.addClass(element, 'hide');
405+
$rootScope.$digest();
406+
407+
expect(element).toHaveClass('red-add');
408+
expect(element).toHaveClass('hide-add');
409+
410+
// When a digest has passed, but no $rAF has been issued yet, .hide hasn't been added to
411+
// the element yet
412+
$animate.removeClass(element, 'hide');
413+
$rootScope.$digest();
414+
$$rAF.flush();
415+
416+
expect(element).not.toHaveClass('hide-add hide-add-active');
417+
expect(element).toHaveClass('hide-remove hide-remove-active');
418+
419+
//End the animation process
420+
browserTrigger(element, 'transitionend',
421+
{ timeStamp: Date.now() + 1000, elapsedTime: 2 });
422+
$animate.flush();
423+
424+
expect(element).not.toHaveClass('hide-add-active red-add-active');
425+
expect(element).toHaveClass('red');
426+
expect(element).not.toHaveClass('hide');
427+
}));
428+
390429
});
391430

392431
describe('JS animations', function() {

0 commit comments

Comments
 (0)