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

Commit 41a2d5b

Browse files
committed
fix($animate): ensure staggering animations understand multiple delay values
1 parent e53ff43 commit 41a2d5b

File tree

2 files changed

+151
-12
lines changed

2 files changed

+151
-12
lines changed

src/ngAnimate/animate.js

+36-11
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,8 @@ angular.module('ngAnimate', ['ng'])
789789
var data = cacheKey ? lookupCache[cacheKey] : null;
790790
if(!data) {
791791
var transitionDuration = 0, transitionDelay = 0,
792-
animationDuration = 0, animationDelay = 0;
792+
animationDuration = 0, animationDelay = 0,
793+
transitionDelayStyle, animationDelayStyle;
793794

794795
//we want all the styles defined before and after
795796
forEach(element, function(element) {
@@ -799,9 +800,13 @@ angular.module('ngAnimate', ['ng'])
799800
transitionDuration = Math.max(parseMaxTime(elementStyles[transitionProp + durationKey]), transitionDuration);
800801

801802
if(!onlyCheckTransition) {
802-
transitionDelay = Math.max(parseMaxTime(elementStyles[transitionProp + delayKey]), transitionDelay);
803+
transitionDelayStyle = elementStyles[transitionProp + delayKey];
803804

804-
animationDelay = Math.max(parseMaxTime(elementStyles[animationProp + delayKey]), animationDelay);
805+
transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay);
806+
807+
animationDelayStyle = elementStyles[animationProp + delayKey];
808+
809+
animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay);
805810

806811
var aDuration = parseMaxTime(elementStyles[animationProp + durationKey]);
807812

@@ -815,9 +820,11 @@ angular.module('ngAnimate', ['ng'])
815820
});
816821
data = {
817822
total : 0,
823+
transitionDelayStyle: transitionDelayStyle,
818824
transitionDelay : transitionDelay,
819-
animationDelay : animationDelay,
820825
transitionDuration : transitionDuration,
826+
animationDelayStyle: animationDelayStyle,
827+
animationDelay : animationDelay,
821828
animationDuration : animationDuration
822829
};
823830
if(cacheKey) {
@@ -905,16 +912,25 @@ angular.module('ngAnimate', ['ng'])
905912

906913
if(timings.transitionDuration > 0) {
907914
node.style[transitionProp + propertyKey] = '';
908-
if(ii > 0 && stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
909-
formerStyle = applyStyle(node, prefix + 'transition-delay: ' +
910-
(ii * stagger.transitionDelay + timings.transitionDelay) + 's');
911-
}
912915
}
913916

914-
if(ii > 0 && stagger.animationDelay > 0 && stagger.animationDuration === 0) {
915-
formerStyle = applyStyle(node, prefix + 'animation-delay: ' +
916-
(ii * stagger.animationDelay + timings.animationDelay) + 's');
917+
if(ii > 0) {
918+
var staggerStyle = '';
919+
if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
920+
staggerStyle += prefix + 'transition-delay: ' +
921+
prepareStaggerDelay(timings.transitionDelayStyle, stagger.transitionDelay, ii) + '; ';
922+
}
923+
924+
if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
925+
staggerStyle += prefix + 'animation-delay: ' +
926+
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
927+
}
928+
929+
if(staggerStyle.length > 0) {
930+
formerStyle = applyStyle(node, staggerStyle);
931+
}
917932
}
933+
918934
element.addClass(activeClassName);
919935
});
920936

@@ -948,6 +964,15 @@ angular.module('ngAnimate', ['ng'])
948964
done();
949965
}
950966

967+
function prepareStaggerDelay(delayStyle, staggerDelay, index) {
968+
var style = '';
969+
angular.forEach(delayStyle.split(','), function(val, i) {
970+
style += (i > 0 ? ',' : '') +
971+
(index * staggerDelay + parseInt(val, 10)) + 's';
972+
});
973+
return style;
974+
}
975+
951976
function onAnimationProgress(event) {
952977
event.stopPropagation();
953978
var ev = event.originalEvent || event;

test/ngAnimate/animateSpec.js

+115-1
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ describe("ngAnimate", function() {
697697

698698
ss.addRule('.ani.ng-enter, .ani.ng-leave, .ani-fake.ng-enter, .ani-fake.ng-leave',
699699
'-webkit-animation:1s my_animation;' +
700-
'transition:1s my_animation;');
700+
'animation:1s my_animation;');
701701

702702
ss.addRule('.ani.ng-enter-stagger, .ani.ng-leave-stagger',
703703
'-webkit-animation-delay:0.1s;' +
@@ -747,6 +747,40 @@ describe("ngAnimate", function() {
747747
expect(elements[3].attr('style')).not.toMatch(/animation-delay: 0\.3\d*s/);
748748
expect(elements[4].attr('style')).not.toMatch(/animation-delay: 0\.4\d*s/);
749749
}));
750+
751+
it("should stagger items when multiple animation durations/delays are defined",
752+
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
753+
754+
if(!$sniffer.transitions) return;
755+
756+
$animate.enabled(true);
757+
758+
ss.addRule('.ani.ng-enter, .ani.ng-leave',
759+
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
760+
'animation:my_animation 1s 1s, your_animation 1s 2s;');
761+
762+
ss.addRule('.ani.ng-enter-stagger, .ani.ng-leave-stagger',
763+
'-webkit-animation-delay:0.1s;' +
764+
'animation-delay:0.1s;');
765+
766+
var container = $compile(html('<div></div>'))($rootScope);
767+
768+
var elements = [];
769+
for(var i = 0; i < 4; i++) {
770+
var newScope = $rootScope.$new();
771+
var element = $compile('<div class="ani"></div>')(newScope);
772+
$animate.enter(element, container);
773+
elements.push(element);
774+
};
775+
776+
$rootScope.$digest();
777+
$timeout.flush();
778+
779+
expect(elements[0].attr('style')).toBeFalsy();
780+
expect(elements[1].attr('style')).toMatch(/animation-delay: 1\.1\d*s,\s*2\.1\d*s/);
781+
expect(elements[2].attr('style')).toMatch(/animation-delay: 1\.2\d*s,\s*2\.2\d*s/);
782+
expect(elements[3].attr('style')).toMatch(/animation-delay: 1\.3\d*s,\s*2\.3\d*s/);
783+
}));
750784
});
751785

752786
describe("Transitions", function() {
@@ -950,7 +984,87 @@ describe("ngAnimate", function() {
950984
expect(elements[3].attr('style')).not.toMatch(/transition-delay: 0\.3\d*s/);
951985
expect(elements[4].attr('style')).not.toMatch(/transition-delay: 0\.4\d*s/);
952986
}));
987+
988+
it("should stagger items when multiple transition durations/delays are defined",
989+
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
990+
991+
if(!$sniffer.transitions) return;
992+
993+
$animate.enabled(true);
994+
995+
ss.addRule('.ani.ng-enter, .ani.ng-leave',
996+
'-webkit-transition:1s linear color 2s, 3s linear font-size 4s;' +
997+
'transition:1s linear color 2s, 3s linear font-size 4s;');
998+
999+
ss.addRule('.ani.ng-enter-stagger, .ani.ng-leave-stagger',
1000+
'-webkit-transition-delay:0.1s;' +
1001+
'transition-delay:0.1s;');
1002+
1003+
var container = $compile(html('<div></div>'))($rootScope);
1004+
1005+
var elements = [];
1006+
for(var i = 0; i < 4; i++) {
1007+
var newScope = $rootScope.$new();
1008+
var element = $compile('<div class="ani"></div>')(newScope);
1009+
$animate.enter(element, container);
1010+
elements.push(element);
1011+
};
1012+
1013+
$rootScope.$digest();
1014+
$timeout.flush();
1015+
1016+
expect(elements[0].attr('style')).toBeFalsy();
1017+
expect(elements[1].attr('style')).toMatch(/transition-delay: 2\.1\d*s,\s*4\.1\d*s/);
1018+
expect(elements[2].attr('style')).toMatch(/transition-delay: 2\.2\d*s,\s*4\.2\d*s/);
1019+
expect(elements[3].attr('style')).toMatch(/transition-delay: 2\.3\d*s,\s*4\.3\d*s/);
1020+
}));
9531021
});
1022+
1023+
it("should apply staggering to both transitions and keyframe animations when used within the same animation",
1024+
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
1025+
1026+
if(!$sniffer.transitions) return;
1027+
1028+
$animate.enabled(true);
1029+
1030+
ss.addRule('.ani.ng-enter, .ani.ng-leave',
1031+
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
1032+
'animation:my_animation 1s 1s, your_animation 1s 2s;' +
1033+
'-webkit-transition:1s linear all 0s;' +
1034+
'transition:1s linear all 1s;');
1035+
1036+
ss.addRule('.ani.ng-enter-stagger, .ani.ng-leave-stagger',
1037+
'-webkit-transition-delay:0.1s;' +
1038+
'transition-delay:0.1s;' +
1039+
'-webkit-animation-delay:0.2s;' +
1040+
'animation-delay:0.2s;');
1041+
1042+
var container = $compile(html('<div></div>'))($rootScope);
1043+
1044+
var elements = [];
1045+
for(var i = 0; i < 3; i++) {
1046+
var newScope = $rootScope.$new();
1047+
var element = $compile('<div class="ani"></div>')(newScope);
1048+
$animate.enter(element, container);
1049+
elements.push(element);
1050+
};
1051+
1052+
$rootScope.$digest();
1053+
$timeout.flush();
1054+
1055+
expect(elements[0].attr('style')).toBeFalsy();
1056+
1057+
expect(elements[1].attr('style')).toMatch(/transition-delay:\s+1.1\d*/);
1058+
expect(elements[1].attr('style')).toMatch(/animation-delay: 1\.2\d*s,\s*2\.2\d*s/);
1059+
1060+
expect(elements[2].attr('style')).toMatch(/transition-delay:\s+1.2\d*/);
1061+
expect(elements[2].attr('style')).toMatch(/animation-delay: 1\.4\d*s,\s*2\.4\d*s/);
1062+
1063+
for(var i = 0; i < 3; i++) {
1064+
browserTrigger(elements[i],'transitionend', { timeStamp: Date.now() + 22000, elapsedTime: 22000 });
1065+
expect(elements[i].attr('style')).toBeFalsy();
1066+
}
1067+
}));
9541068
});
9551069

9561070
describe('animation evaluation', function () {

0 commit comments

Comments
 (0)