fix($animate): ensure staggering animations understand multiple delay values

This commit is contained in:
Matias Niemelä 2013-10-31 11:49:06 -07:00
parent e53ff431e1
commit 41a2d5b30f
2 changed files with 151 additions and 12 deletions

View file

@ -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) {
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);
}
}
if(ii > 0 && stagger.animationDelay > 0 && stagger.animationDuration === 0) {
formerStyle = applyStyle(node, prefix + 'animation-delay: ' +
(ii * stagger.animationDelay + timings.animationDelay) + 's');
}
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;

View file

@ -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 () {