Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 89f081e

Browse files
committed
fix(ngAnimate): ensure ngClass-based classes are always resolved for CSS-enabled animations
1 parent 3333a5c commit 89f081e

File tree

4 files changed

+31
-41
lines changed

4 files changed

+31
-41
lines changed

src/ngAnimate/animateCss.js

+16-40
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@
221221
* ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.})
222222
* * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
223223
* `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
224+
* `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
224225
*
225226
* @return {null|object} an object with a start method and details about the animation. If no animation is detected then a value of `null` will be returned.
226227
*
@@ -563,6 +564,17 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
563564
addRemoveClassName += pendClasses(options.removeClass, '-remove');
564565
}
565566

567+
// there may be a situation where a structural animation is combined together
568+
// with CSS classes that need to resolve before the animation is computed.
569+
// However this means that there is no explicit CSS code to block the animation
570+
// from happening (by setting 0s none in the class name). If this is the case
571+
// we need to apply the classes before the first rAF so we know to continue if
572+
// there actually is a detected transition or keyframe animation
573+
if (options.applyClassesEarly && addRemoveClassName.length) {
574+
applyAnimationClasses(element, options);
575+
addRemoveClassName = '';
576+
}
577+
566578
var setupClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
567579
var fullClassName = classes + ' ' + setupClasses;
568580
var activeClasses = pendClasses(setupClasses, '-active');
@@ -644,10 +656,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
644656
flags.applyTransitionDuration = hasToStyles && (
645657
(flags.hasTransitions && !flags.hasTransitionAll)
646658
|| (flags.hasAnimations && !flags.hasTransitions));
647-
flags.applyAnimationDuration = options.duration && flags.hasAnimations;
648-
flags.applyTransitionDelay = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions);
649-
flags.applyAnimationDelay = truthyTimingValue(options.delay) && flags.hasAnimations;
650-
flags.recalculateTimingStyles = addRemoveClassName.length > 0;
659+
flags.applyAnimationDuration = options.duration && flags.hasAnimations;
660+
flags.applyTransitionDelay = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions);
661+
flags.applyAnimationDelay = truthyTimingValue(options.delay) && flags.hasAnimations;
662+
flags.recalculateTimingStyles = addRemoveClassName.length > 0;
651663

652664
if (flags.applyTransitionDuration || flags.applyAnimationDuration) {
653665
maxDuration = options.duration ? parseFloat(options.duration) : maxDuration;
@@ -666,42 +678,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
666678
}
667679
}
668680

669-
flags.transitionClassBlock = timings.transitionProperty === 'none' &&
670-
timings.transitionDuration === 0;
671-
672-
// there may be a situation where a structural animation is combined together
673-
// with CSS classes that need to resolve before the animation is computed.
674-
// However this means that there is no explicit CSS code to block the animation
675-
// from happening (by setting 0s none in the class name). If this is the case
676-
// we need to apply the classes before the first rAF so we know to continue if
677-
// there actually is a detected transition or keyframe animation
678-
var applyClassesEarly = maxDuration === 0
679-
&& isStructural
680-
&& addRemoveClassName.length > 0
681-
&& !flags.transitionClassBlock;
682-
683-
// this is an early check to avoid having to do another call to getComputedStyle
684-
// call which is expensive. GCS calls are cached to speed things up.
685-
if (!applyClassesEarly && maxDuration === 0 && !flags.recalculateTimingStyles) {
686-
close();
687-
return false;
688-
}
689-
690-
if (applyClassesEarly) {
691-
applyAnimationClasses(element, options);
692-
693-
// no need to calculate this anymore
694-
flags.recalculateTimingStyles = false;
695-
696-
fullClassName = node.className + ' ' + setupClasses;
697-
cacheKey = gcsHashFn(node, fullClassName);
698-
699-
timings = computeTimings(node, fullClassName, cacheKey);
700-
relativeDelay = timings.maxDelay;
701-
maxDelay = Math.max(relativeDelay, 0);
702-
maxDuration = timings.maxDuration;
703-
}
704-
705681
if (maxDuration === 0 && !flags.recalculateTimingStyles) {
706682
close();
707683
return false;

src/ngAnimate/animateCssDriver.js

+5
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,13 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
214214
function prepareRegularAnimation(animationDetails) {
215215
var element = animationDetails.element;
216216
var options = animationDetails.options || {};
217+
217218
options.structural = animationDetails.structural;
218219

220+
// structural animations ensure that the CSS classes are always applied
221+
// before the detection starts.
222+
options.applyClassesEarly = options.structural;
223+
219224
// we special case the leave animation since we want to ensure that
220225
// the element is removed as soon as the animation is over. Otherwise
221226
// a flicker might appear or the element may not be removed at all

test/ngAnimate/animateCssDriverSpec.js

+8
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ describe("ngAnimate $$animateCssDriver", function() {
9595
var runner = driver({ element: element });
9696
expect(isFunction(runner.start)).toBeTruthy();
9797
}));
98+
99+
it("should signal $animateCss to apply the classes early when an event is present", inject(function() {
100+
driver({ element: element, structural: true });
101+
expect(capturedAnimation[1].applyClassesEarly).toBeTruthy();
102+
103+
driver({ element: element });
104+
expect(capturedAnimation[1].applyClassesEarly).toBeFalsy();
105+
}));
98106
});
99107

100108
describe("anchored animations", function() {

test/ngAnimate/animateCssSpec.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1456,7 +1456,7 @@ describe("ngAnimate $animateCss", function() {
14561456
});
14571457
});
14581458

1459-
they('should force the class-based values to be applied early if no transition/keyframe is detected at all',
1459+
they('should force the class-based values to be applied early if no options.applyClassEarly is used as an option',
14601460
['enter', 'leave', 'move'], function(event) {
14611461
inject(function($animateCss, $rootElement, $document) {
14621462

@@ -1468,6 +1468,7 @@ describe("ngAnimate $animateCss", function() {
14681468

14691469
var runner = $animateCss(element, {
14701470
addClass: 'blue',
1471+
applyClassesEarly: true,
14711472
removeClass: 'red',
14721473
event: event,
14731474
structural: true

0 commit comments

Comments
 (0)