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

Commit 0c81e9f

Browse files
committed
fix($animateCss): the transitions options delay value should be applied before class application
When `options.delay` is passed into `$animateCss`the delay style would be applied after the add/remove CSS classes are evaluated (for transitions). At this point it is too late for the delay to be picked up (this functionality however does work with keyfarme animations). This patch ensures that the provided `options.delay` value is applied before the CSS classes are applied to the element. Closes #12584
1 parent 5df80e1 commit 0c81e9f

File tree

2 files changed

+85
-49
lines changed

2 files changed

+85
-49
lines changed

src/ngAnimate/animateCss.js

+17-16
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,18 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
598598
return closeAndReturnNoopAnimator();
599599
}
600600

601+
if (options.delay != null) {
602+
var delayStyle = parseFloat(options.delay);
603+
604+
if (flags.applyTransitionDelay) {
605+
temporaryStyles.push(getCssDelayStyle(delayStyle));
606+
}
607+
608+
if (flags.applyAnimationDelay) {
609+
temporaryStyles.push(getCssDelayStyle(delayStyle, true));
610+
}
611+
}
612+
601613
// we need to recalculate the delay value since we used a pre-emptive negative
602614
// delay value and the delay value is required for the final event checking. This
603615
// property will ensure that this will happen after the RAF phase has passed.
@@ -809,27 +821,16 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
809821
flags.hasAnimations = timings.animationDuration > 0;
810822
}
811823

812-
if (flags.applyTransitionDelay || flags.applyAnimationDelay) {
824+
if (flags.applyAnimationDelay) {
813825
relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)
814826
? parseFloat(options.delay)
815827
: relativeDelay;
816828

817829
maxDelay = Math.max(relativeDelay, 0);
818-
819-
var delayStyle;
820-
if (flags.applyTransitionDelay) {
821-
timings.transitionDelay = relativeDelay;
822-
delayStyle = getCssDelayStyle(relativeDelay);
823-
temporaryStyles.push(delayStyle);
824-
node.style[delayStyle[0]] = delayStyle[1];
825-
}
826-
827-
if (flags.applyAnimationDelay) {
828-
timings.animationDelay = relativeDelay;
829-
delayStyle = getCssDelayStyle(relativeDelay, true);
830-
temporaryStyles.push(delayStyle);
831-
node.style[delayStyle[0]] = delayStyle[1];
832-
}
830+
timings.animationDelay = relativeDelay;
831+
delayStyle = getCssDelayStyle(relativeDelay, true);
832+
temporaryStyles.push(delayStyle);
833+
node.style[delayStyle[0]] = delayStyle[1];
833834
}
834835

835836
maxDelayTime = maxDelay * ONE_SECOND;

test/ngAnimate/animateCssSpec.js

+68-33
Original file line numberDiff line numberDiff line change
@@ -1671,11 +1671,13 @@ describe("ngAnimate $animateCss", function() {
16711671

16721672
describe("options", function() {
16731673
var element;
1674-
beforeEach(inject(function($rootElement, $$body) {
1675-
$$body.append($rootElement);
1674+
beforeEach(module(function() {
1675+
return function($rootElement, $$body) {
1676+
$$body.append($rootElement);
16761677

1677-
element = jqLite('<div></div>');
1678-
$rootElement.append(element);
1678+
element = jqLite('<div></div>');
1679+
$rootElement.append(element);
1680+
};
16791681
}));
16801682

16811683
describe("[$$skipPreparationClasses]", function() {
@@ -1862,7 +1864,6 @@ describe("ngAnimate $animateCss", function() {
18621864
animator.start();
18631865
triggerAnimationStartFrame();
18641866

1865-
18661867
var prop = element.css('transition-delay');
18671868
expect(prop).toEqual('500s');
18681869
}));
@@ -1978,6 +1979,53 @@ describe("ngAnimate $animateCss", function() {
19781979
expect(element.css('transition-delay')).toEqual('10s');
19791980
}));
19801981

1982+
it("should apply the keyframe and transition duration value before the CSS classes are applied", function() {
1983+
var classSpy = jasmine.createSpy();
1984+
module(function($provide) {
1985+
$provide.value('$$jqLite', {
1986+
addClass: function() {
1987+
classSpy();
1988+
},
1989+
removeClass: function() {
1990+
classSpy();
1991+
}
1992+
});
1993+
});
1994+
inject(function($animateCss, $rootElement) {
1995+
element.addClass('element');
1996+
ss.addRule('.element', '-webkit-animation:3s keyframe_animation;' +
1997+
'animation:3s keyframe_animation;' +
1998+
'transition:5s linear all;');
1999+
2000+
var options = {
2001+
delay: 2,
2002+
duration: 2,
2003+
addClass: 'superman',
2004+
$$skipPreparationClasses: true,
2005+
structural: true
2006+
};
2007+
var animator = $animateCss(element, options);
2008+
2009+
expect(element.attr('style') || '').not.toContain('animation-delay');
2010+
expect(element.attr('style') || '').not.toContain('transition-delay');
2011+
expect(classSpy).not.toHaveBeenCalled();
2012+
2013+
//redefine the classSpy to assert that the delay values have been
2014+
//applied before the classes are added
2015+
var assertionsRun = false;
2016+
classSpy = function() {
2017+
assertionsRun = true;
2018+
expect(element.css(prefix + 'animation-delay')).toEqual('2s');
2019+
expect(element.css('transition-delay')).toEqual('2s');
2020+
expect(element).not.toHaveClass('superman');
2021+
};
2022+
2023+
animator.start();
2024+
triggerAnimationStartFrame();
2025+
expect(assertionsRun).toBe(true);
2026+
});
2027+
});
2028+
19812029
it("should apply blocking before the animation starts, but then apply the detected delay when options.delay is true",
19822030
inject(function($animateCss, $rootElement) {
19832031

@@ -1995,41 +2043,28 @@ describe("ngAnimate $animateCss", function() {
19952043
animator.start();
19962044
triggerAnimationStartFrame();
19972045

1998-
expect(element.css('transition-delay')).toEqual('1s');
2046+
expect(element.attr('style') || '').not.toContain('transition-delay');
19992047
}));
20002048

2001-
they("should consider a negative value when delay:true is used with a $prop animation", {
2002-
'transition': function() {
2003-
return {
2004-
prop: 'transition-delay',
2005-
css: 'transition:2s linear all; transition-delay: -1s'
2006-
};
2007-
},
2008-
'keyframe': function(prefix) {
2009-
return {
2010-
prop: prefix + 'animation-delay',
2011-
css: prefix + 'animation:2s keyframe_animation; ' + prefix + 'animation-delay: -1s;'
2012-
};
2013-
}
2014-
}, function(testDetailsFactory) {
2049+
it("should consider a negative value when delay:true is used with a keyframe animation",
20152050
inject(function($animateCss, $rootElement) {
2016-
var testDetails = testDetailsFactory(prefix);
20172051

2018-
ss.addRule('.ng-enter', testDetails.css);
2019-
var options = {
2020-
delay: true,
2021-
event: 'enter',
2022-
structural: true
2023-
};
2052+
ss.addRule('.ng-enter', prefix + 'animation:2s keyframe_animation; ' +
2053+
prefix + 'animation-delay: -1s;');
20242054

2025-
var animator = $animateCss(element, options);
2055+
var options = {
2056+
delay: true,
2057+
event: 'enter',
2058+
structural: true
2059+
};
20262060

2027-
animator.start();
2028-
triggerAnimationStartFrame();
2061+
var animator = $animateCss(element, options);
20292062

2030-
expect(element.css(testDetails.prop)).toContain('-1s');
2031-
});
2032-
});
2063+
animator.start();
2064+
triggerAnimationStartFrame();
2065+
2066+
expect(element.css(prefix + 'animation-delay')).toContain('-1s');
2067+
}));
20332068

20342069
they("should consider a negative value when a negative option delay is provided for a $prop animation", {
20352070
'transition': function() {

0 commit comments

Comments
 (0)