aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatias Niemelä2013-10-31 11:49:06 -0700
committerMatias Niemelä2013-11-05 21:44:25 -0500
commit41a2d5b30f4feb90651eb577cf44852a6d2be72c (patch)
tree1bc4f99553a0acc140ee36e3ea15be235e7d07c3
parente53ff431e1472c0b2d5405d267d4e403ca31087e (diff)
downloadangular.js-41a2d5b30f4feb90651eb577cf44852a6d2be72c.tar.bz2
fix($animate): ensure staggering animations understand multiple delay values
-rw-r--r--src/ngAnimate/animate.js47
-rw-r--r--test/ngAnimate/animateSpec.js116
2 files changed, 151 insertions, 12 deletions
diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js
index ad192a5b..3bb6ff31 100644
--- a/src/ngAnimate/animate.js
+++ b/src/ngAnimate/animate.js
@@ -789,7 +789,8 @@ angular.module('ngAnimate', ['ng'])
var data = cacheKey ? lookupCache[cacheKey] : null;
if(!data) {
var transitionDuration = 0, transitionDelay = 0,
- animationDuration = 0, animationDelay = 0;
+ animationDuration = 0, animationDelay = 0,
+ transitionDelayStyle, animationDelayStyle;
//we want all the styles defined before and after
forEach(element, function(element) {
@@ -799,9 +800,13 @@ angular.module('ngAnimate', ['ng'])
transitionDuration = Math.max(parseMaxTime(elementStyles[transitionProp + durationKey]), transitionDuration);
if(!onlyCheckTransition) {
- transitionDelay = Math.max(parseMaxTime(elementStyles[transitionProp + delayKey]), transitionDelay);
+ transitionDelayStyle = elementStyles[transitionProp + delayKey];
- animationDelay = Math.max(parseMaxTime(elementStyles[animationProp + delayKey]), animationDelay);
+ transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay);
+
+ animationDelayStyle = elementStyles[animationProp + delayKey];
+
+ animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay);
var aDuration = parseMaxTime(elementStyles[animationProp + durationKey]);
@@ -815,9 +820,11 @@ angular.module('ngAnimate', ['ng'])
});
data = {
total : 0,
+ transitionDelayStyle: transitionDelayStyle,
transitionDelay : transitionDelay,
- animationDelay : animationDelay,
transitionDuration : transitionDuration,
+ animationDelayStyle: animationDelayStyle,
+ animationDelay : animationDelay,
animationDuration : animationDuration
};
if(cacheKey) {
@@ -905,16 +912,25 @@ angular.module('ngAnimate', ['ng'])
if(timings.transitionDuration > 0) {
node.style[transitionProp + propertyKey] = '';
- if(ii > 0 && stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
- formerStyle = applyStyle(node, prefix + 'transition-delay: ' +
- (ii * stagger.transitionDelay + timings.transitionDelay) + 's');
- }
}
- if(ii > 0 && stagger.animationDelay > 0 && stagger.animationDuration === 0) {
- formerStyle = applyStyle(node, prefix + 'animation-delay: ' +
- (ii * stagger.animationDelay + timings.animationDelay) + 's');
+ if(ii > 0) {
+ var staggerStyle = '';
+ if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
+ staggerStyle += prefix + 'transition-delay: ' +
+ prepareStaggerDelay(timings.transitionDelayStyle, stagger.transitionDelay, ii) + '; ';
+ }
+
+ if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
+ staggerStyle += prefix + 'animation-delay: ' +
+ prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
+ }
+
+ if(staggerStyle.length > 0) {
+ formerStyle = applyStyle(node, staggerStyle);
+ }
}
+
element.addClass(activeClassName);
});
@@ -948,6 +964,15 @@ angular.module('ngAnimate', ['ng'])
done();
}
+ function prepareStaggerDelay(delayStyle, staggerDelay, index) {
+ var style = '';
+ angular.forEach(delayStyle.split(','), function(val, i) {
+ style += (i > 0 ? ',' : '') +
+ (index * staggerDelay + parseInt(val, 10)) + 's';
+ });
+ return style;
+ }
+
function onAnimationProgress(event) {
event.stopPropagation();
var ev = event.originalEvent || event;
diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js
index ec37d8c3..f3523692 100644
--- a/test/ngAnimate/animateSpec.js
+++ b/test/ngAnimate/animateSpec.js
@@ -697,7 +697,7 @@ describe("ngAnimate", function() {
ss.addRule('.ani.ng-enter, .ani.ng-leave, .ani-fake.ng-enter, .ani-fake.ng-leave',
'-webkit-animation:1s my_animation;' +
- 'transition:1s my_animation;');
+ 'animation:1s my_animation;');
ss.addRule('.ani.ng-enter-stagger, .ani.ng-leave-stagger',
'-webkit-animation-delay:0.1s;' +
@@ -747,6 +747,40 @@ describe("ngAnimate", function() {
expect(elements[3].attr('style')).not.toMatch(/animation-delay: 0\.3\d*s/);
expect(elements[4].attr('style')).not.toMatch(/animation-delay: 0\.4\d*s/);
}));
+
+ it("should stagger items when multiple animation durations/delays are defined",
+ inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
+
+ if(!$sniffer.transitions) return;
+
+ $animate.enabled(true);
+
+ ss.addRule('.ani.ng-enter, .ani.ng-leave',
+ '-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
+ 'animation:my_animation 1s 1s, your_animation 1s 2s;');
+
+ ss.addRule('.ani.ng-enter-stagger, .ani.ng-leave-stagger',
+ '-webkit-animation-delay:0.1s;' +
+ 'animation-delay:0.1s;');
+
+ var container = $compile(html('<div></div>'))($rootScope);
+
+ var elements = [];
+ for(var i = 0; i < 4; i++) {
+ var newScope = $rootScope.$new();
+ var element = $compile('<div class="ani"></div>')(newScope);
+ $animate.enter(element, container);
+ elements.push(element);
+ };
+
+ $rootScope.$digest();
+ $timeout.flush();
+
+ expect(elements[0].attr('style')).toBeFalsy();
+ expect(elements[1].attr('style')).toMatch(/animation-delay: 1\.1\d*s,\s*2\.1\d*s/);
+ expect(elements[2].attr('style')).toMatch(/animation-delay: 1\.2\d*s,\s*2\.2\d*s/);
+ expect(elements[3].attr('style')).toMatch(/animation-delay: 1\.3\d*s,\s*2\.3\d*s/);
+ }));
});
describe("Transitions", function() {
@@ -950,7 +984,87 @@ describe("ngAnimate", function() {
expect(elements[3].attr('style')).not.toMatch(/transition-delay: 0\.3\d*s/);
expect(elements[4].attr('style')).not.toMatch(/transition-delay: 0\.4\d*s/);
}));
+
+ it("should stagger items when multiple transition durations/delays are defined",
+ inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
+
+ if(!$sniffer.transitions) return;
+
+ $animate.enabled(true);
+
+ ss.addRule('.ani.ng-enter, .ani.ng-leave',
+ '-webkit-transition:1s linear color 2s, 3s linear font-size 4s;' +
+ 'transition:1s linear color 2s, 3s linear font-size 4s;');
+
+ ss.addRule('.ani.ng-enter-stagger, .ani.ng-leave-stagger',
+ '-webkit-transition-delay:0.1s;' +
+ 'transition-delay:0.1s;');
+
+ var container = $compile(html('<div></div>'))($rootScope);
+
+ var elements = [];
+ for(var i = 0; i < 4; i++) {
+ var newScope = $rootScope.$new();
+ var element = $compile('<div class="ani"></div>')(newScope);
+ $animate.enter(element, container);
+ elements.push(element);
+ };
+
+ $rootScope.$digest();
+ $timeout.flush();
+
+ expect(elements[0].attr('style')).toBeFalsy();
+ expect(elements[1].attr('style')).toMatch(/transition-delay: 2\.1\d*s,\s*4\.1\d*s/);
+ expect(elements[2].attr('style')).toMatch(/transition-delay: 2\.2\d*s,\s*4\.2\d*s/);
+ expect(elements[3].attr('style')).toMatch(/transition-delay: 2\.3\d*s,\s*4\.3\d*s/);
+ }));
});
+
+ it("should apply staggering to both transitions and keyframe animations when used within the same animation",
+ inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
+
+ if(!$sniffer.transitions) return;
+
+ $animate.enabled(true);
+
+ ss.addRule('.ani.ng-enter, .ani.ng-leave',
+ '-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
+ 'animation:my_animation 1s 1s, your_animation 1s 2s;' +
+ '-webkit-transition:1s linear all 0s;' +
+ 'transition:1s linear all 1s;');
+
+ ss.addRule('.ani.ng-enter-stagger, .ani.ng-leave-stagger',
+ '-webkit-transition-delay:0.1s;' +
+ 'transition-delay:0.1s;' +
+ '-webkit-animation-delay:0.2s;' +
+ 'animation-delay:0.2s;');
+
+ var container = $compile(html('<div></div>'))($rootScope);
+
+ var elements = [];
+ for(var i = 0; i < 3; i++) {
+ var newScope = $rootScope.$new();
+ var element = $compile('<div class="ani"></div>')(newScope);
+ $animate.enter(element, container);
+ elements.push(element);
+ };
+
+ $rootScope.$digest();
+ $timeout.flush();
+
+ expect(elements[0].attr('style')).toBeFalsy();
+
+ expect(elements[1].attr('style')).toMatch(/transition-delay:\s+1.1\d*/);
+ expect(elements[1].attr('style')).toMatch(/animation-delay: 1\.2\d*s,\s*2\.2\d*s/);
+
+ expect(elements[2].attr('style')).toMatch(/transition-delay:\s+1.2\d*/);
+ expect(elements[2].attr('style')).toMatch(/animation-delay: 1\.4\d*s,\s*2\.4\d*s/);
+
+ for(var i = 0; i < 3; i++) {
+ browserTrigger(elements[i],'transitionend', { timeStamp: Date.now() + 22000, elapsedTime: 22000 });
+ expect(elements[i].attr('style')).toBeFalsy();
+ }
+ }));
});
describe('animation evaluation', function () {