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

Commit 2cf71b0

Browse files
committed
fix($animateCss): respect transition styles already on the element
Previously, $animateCss wouldn't use transition styles that were on the element before the animation process started. Precisely, transition property, timing-function and delay were overwritten in the process.
1 parent 2e23a3c commit 2cf71b0

File tree

4 files changed

+119
-44
lines changed

4 files changed

+119
-44
lines changed

src/ngAnimate/.jshintrc

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
"TRANSITION_DURATION_PROP": false,
3333
"TRANSITION_DELAY_PROP": false,
34+
"TRANSITION_TIMING_PROP": false,
35+
"TRANSITION_PROPERTY_PROP": false,
3436
"TRANSITION_PROP": false,
3537
"PROPERTY_KEY": false,
3638
"DURATION_KEY": false,

src/ngAnimate/animateCss.js

+27-32
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,13 @@ var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
223223
var CLOSING_TIME_BUFFER = 1.5;
224224

225225
var DETECT_CSS_PROPERTIES = {
226-
transitionDuration: TRANSITION_DURATION_PROP,
227-
transitionDelay: TRANSITION_DELAY_PROP,
228-
transitionProperty: TRANSITION_PROP + PROPERTY_KEY,
229-
animationDuration: ANIMATION_DURATION_PROP,
230-
animationDelay: ANIMATION_DELAY_PROP,
231-
animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY
226+
transitionDuration: TRANSITION_DURATION_PROP,
227+
transitionDelay: TRANSITION_DELAY_PROP,
228+
transitionProperty: TRANSITION_PROP + PROPERTY_KEY,
229+
transitionTimingFunction: TRANSITION_PROP + TIMING_KEY,
230+
animationDuration: ANIMATION_DURATION_PROP,
231+
animationDelay: ANIMATION_DELAY_PROP,
232+
animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY
232233
};
233234

234235
var DETECT_STAGGER_CSS_PROPERTIES = {
@@ -292,14 +293,14 @@ function truthyTimingValue(val) {
292293
return val === 0 || val != null;
293294
}
294295

295-
function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
296+
function getCssTransitionStyle(timings, duration) {
296297
var style = TRANSITION_PROP;
297298
var value = duration + 's';
298-
if (applyOnlyDuration) {
299-
style += DURATION_KEY;
300-
} else {
301-
value += ' linear all';
302-
}
299+
300+
value += ' ' + timings[TRANSITION_TIMING_PROP];
301+
value += ' ' + timings[TRANSITION_PROPERTY_PROP];
302+
value += timings[TRANSITION_DELAY_PROP] ? ' ' + timings[TRANSITION_DELAY_PROP] + 's' : '';
303+
303304
return [style, value];
304305
}
305306

@@ -550,15 +551,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
550551
temporaryStyles.push(transitionStyle);
551552
}
552553

553-
if (options.duration >= 0) {
554-
applyOnlyDuration = node.style[TRANSITION_PROP].length > 0;
555-
var durationStyle = getCssTransitionDurationStyle(options.duration, applyOnlyDuration);
556-
557-
// we set the duration so that it will be picked up by getComputedStyle later
558-
applyInlineStyle(node, durationStyle);
559-
temporaryStyles.push(durationStyle);
560-
}
561-
562554
if (options.keyframeStyle) {
563555
var keyframeStyle = [ANIMATION_PROP, options.keyframeStyle];
564556
applyInlineStyle(node, keyframeStyle);
@@ -571,8 +563,18 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
571563
: gcsLookup.count(cacheKey)
572564
: 0;
573565

574-
var isFirst = itemIndex === 0;
566+
var timings = computeTimings(node, fullClassName, cacheKey);
567+
568+
if (options.duration > 0) {
569+
// Duration in options overwrites duration set in style
570+
timings.transitionDuration = options.duration;
571+
}
575572

573+
var relativeDelay = timings.maxDelay;
574+
maxDelay = Math.max(relativeDelay, 0);
575+
maxDuration = timings.maxDuration;
576+
577+
var isFirst = itemIndex === 0;
576578
// this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY
577579
// without causing any combination of transitions to kick in. By adding a negative delay value
578580
// it forces the setup class' transition to end immediately. We later then remove the negative
@@ -583,17 +585,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
583585
blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
584586
}
585587

586-
var timings = computeTimings(node, fullClassName, cacheKey);
587-
var relativeDelay = timings.maxDelay;
588-
maxDelay = Math.max(relativeDelay, 0);
589-
maxDuration = timings.maxDuration;
590-
591588
var flags = {};
592589
flags.hasTransitions = timings.transitionDuration > 0;
593590
flags.hasAnimations = timings.animationDuration > 0;
594-
flags.hasTransitionAll = flags.hasTransitions && timings.transitionProperty == 'all';
595-
flags.applyTransitionDuration = hasToStyles && (
596-
(flags.hasTransitions && !flags.hasTransitionAll)
591+
flags.applyTransitionDuration = options.duration > 0 || hasToStyles && (flags.hasTransitions
597592
|| (flags.hasAnimations && !flags.hasTransitions));
598593
flags.applyAnimationDuration = options.duration && flags.hasAnimations;
599594
flags.applyTransitionDelay = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions);
@@ -606,15 +601,15 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
606601
if (flags.applyTransitionDuration) {
607602
flags.hasTransitions = true;
608603
timings.transitionDuration = maxDuration;
609-
applyOnlyDuration = node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0;
610-
temporaryStyles.push(getCssTransitionDurationStyle(maxDuration, applyOnlyDuration));
604+
temporaryStyles.push(getCssTransitionStyle(timings, maxDuration));
611605
}
612606

613607
if (flags.applyAnimationDuration) {
614608
flags.hasAnimations = true;
615609
timings.animationDuration = maxDuration;
616610
temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration));
617611
}
612+
618613
}
619614

620615
if (maxDuration === 0 && !flags.recalculateTimingStyles) {

src/ngAnimate/shared.js

+2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
6666
var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
6767
var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
6868
var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
69+
var TRANSITION_TIMING_PROP = TRANSITION_PROP + TIMING_KEY;
70+
var TRANSITION_PROPERTY_PROP = TRANSITION_PROP + PROPERTY_KEY;
6971

7072
var isPromiseLike = function(p) {
7173
return p && p.then ? true : false;

test/ngAnimate/animateCssSpec.js

+88-12
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,86 @@ describe("ngAnimate $animateCss", function() {
634634
keyframeProgress(element, 1, 20);
635635
assertAnimationComplete(true);
636636
}));
637+
638+
it("should apply all transition shorthand properties that are already on the element",
639+
inject(function($animateCss, $rootElement) {
640+
641+
ss.addRule('.action', 'transition: color 1s cubic-bezier(0.25, 0.1, 0.25, 1) 5s;');
642+
element.addClass('action');
643+
644+
var options = {
645+
to: { background: 'blue' }
646+
};
647+
648+
var animator = $animateCss(element, options);
649+
animator.start();
650+
triggerAnimationStartFrame();
651+
652+
expect(element.css('transition-duration')).toMatch('1s');
653+
expect(element.css('transition-delay')).toMatch('5s');
654+
expect(element.css('transition-property')).toMatch('color');
655+
expect(element.css('transition-timing-function')).toBe('cubic-bezier(0.25, 0.1, 0.25, 1)');
656+
}));
657+
658+
it("should apply all explicit transition properties that are already on the element",
659+
inject(function($animateCss, $rootElement) {
660+
661+
ss.addRule('.action', 'transition-duration: 1s;' +
662+
'transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);' +
663+
'transition-property: color;' +
664+
'transition-delay: 5s');
665+
element.addClass('action');
666+
667+
var options = {
668+
to: { background: 'blue' }
669+
};
670+
671+
var animator = $animateCss(element, options);
672+
animator.start();
673+
triggerAnimationStartFrame();
674+
675+
expect(element.css('transition-duration')).toMatch('1s');
676+
expect(element.css('transition-delay')).toMatch('5s');
677+
expect(element.css('transition-property')).toMatch('color');
678+
expect(element.css('transition-timing-function')).toBe('cubic-bezier(0.25, 0.1, 0.25, 1)');
679+
}));
680+
681+
it("should use the default transition-property (spec: all) if none is supplied in shorthand",
682+
inject(function($animateCss, $rootElement) {
683+
684+
ss.addRule('.action', 'transition: 1s ease');
685+
element.addClass('action');
686+
687+
var options = {
688+
to: { background: 'blue' }
689+
};
690+
691+
var animator = $animateCss(element, options);
692+
animator.start();
693+
triggerAnimationStartFrame();
694+
695+
expect(element.css('transition-property')).toBe('all');
696+
}));
697+
698+
it("should use the default easing (spec: ease) if none is supplied in shorthand",
699+
inject(function($animateCss, $rootElement) {
700+
701+
ss.addRule('.action', 'transition: color 1s');
702+
element.addClass('action');
703+
704+
var options = {
705+
to: { background: 'blue' }
706+
};
707+
708+
var animator = $animateCss(element, options);
709+
animator.start();
710+
triggerAnimationStartFrame();
711+
712+
// IE reports ease in cubic-bezier form
713+
expect(element.css('transition-timing-function')).toBeOneOf('ease', 'cubic-bezier(0.25, 0.1, 0.25, 1)');
714+
}));
715+
716+
637717
});
638718

639719
describe("staggering", function() {
@@ -1855,7 +1935,7 @@ describe("ngAnimate $animateCss", function() {
18551935

18561936
var style = element.attr('style');
18571937
expect(style).toContain('3000s');
1858-
expect(style).toContain('linear');
1938+
expect(element.css('transition-timing-function')).toBeOneOf('ease', 'cubic-bezier(0.25, 0.1, 0.25, 1)');
18591939
}));
18601940

18611941
it("should be applied to a CSS keyframe animation directly if keyframes are detected within the CSS class",
@@ -1961,7 +2041,7 @@ describe("ngAnimate $animateCss", function() {
19612041
expect(style).toMatch(/animation(?:-duration)?:\s*4s/);
19622042
expect(element.css('transition-duration')).toMatch('4s');
19632043
expect(element.css('transition-property')).toMatch('all');
1964-
expect(style).toContain('linear');
2044+
expect(element.css('transition-timing-function')).toBeOneOf('linear', 'cubic-bezier(0, 0, 1, 1)');
19652045
}));
19662046
});
19672047

@@ -2125,7 +2205,7 @@ describe("ngAnimate $animateCss", function() {
21252205
var animator = $animateCss(element, options);
21262206

21272207
expect(element.attr('style') || '').not.toContain('animation-delay');
2128-
expect(element.attr('style') || '').not.toContain('transition-delay');
2208+
expect(element.css('transition-delay')).toEqual('-2s');
21292209
expect(classSpy).not.toHaveBeenCalled();
21302210

21312211
//redefine the classSpy to assert that the delay values have been
@@ -2303,10 +2383,9 @@ describe("ngAnimate $animateCss", function() {
23032383
animator.start();
23042384
triggerAnimationStartFrame();
23052385

2306-
var style = element.attr('style');
23072386
expect(element.css('transition-duration')).toMatch('4s');
23082387
expect(element.css('transition-property')).toMatch('color');
2309-
expect(style).toContain('ease-in');
2388+
expect(element.css('transition-timing-function')).toBeOneOf('ease-in', 'cubic-bezier(0.42, 0, 1, 1)');
23102389
}));
23112390

23122391
it("should give priority to the provided delay value, but only update the delay style itself",
@@ -2557,11 +2636,9 @@ describe("ngAnimate $animateCss", function() {
25572636
animator.start();
25582637
triggerAnimationStartFrame();
25592638

2560-
2561-
var style = element.attr('style');
25622639
expect(element.css('transition-duration')).toMatch('2.5s');
25632640
expect(element.css('transition-property')).toMatch('all');
2564-
expect(style).toContain('linear');
2641+
expect(element.css('transition-timing-function')).toBeOneOf('ease', 'cubic-bezier(0.25, 0.1, 0.25, 1)');
25652642
}));
25662643

25672644
it("should remove all inline transition styling when an animation completes",
@@ -2692,7 +2769,7 @@ describe("ngAnimate $animateCss", function() {
26922769
it("should apply a transition duration if the existing transition duration's property value is not 'all'",
26932770
inject(function($animateCss, $rootElement) {
26942771

2695-
ss.addRule('.ng-enter', 'transition: 1s linear color');
2772+
ss.addRule('.ng-enter', 'transition: 1s cubic-bezier(0.25, 0.1, 0.25, 1) color');
26962773

26972774
var emptyObject = {};
26982775
var options = {
@@ -2706,10 +2783,9 @@ describe("ngAnimate $animateCss", function() {
27062783
triggerAnimationStartFrame();
27072784

27082785

2709-
var style = element.attr('style');
27102786
expect(element.css('transition-duration')).toMatch('1s');
2711-
expect(element.css('transition-property')).toMatch('all');
2712-
expect(style).toContain('linear');
2787+
expect(element.css('transition-property')).toMatch('color');
2788+
expect(element.css('transition-timing-function')).toBe('cubic-bezier(0.25, 0.1, 0.25, 1)');
27132789
}));
27142790

27152791
it("should apply a transition duration and an animation duration if duration + styles options are provided for a matching keyframe animation",

0 commit comments

Comments
 (0)