mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-11 08:13:10 +00:00
fix(ngAnimate): make sure that the class value passed into addClass/removeClass is the base class string value
This commit is contained in:
parent
f61ff69519
commit
2430347ece
2 changed files with 153 additions and 120 deletions
|
|
@ -489,6 +489,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
done:done
|
done:done
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var baseClassName = className;
|
||||||
if(event == 'addClass') {
|
if(event == 'addClass') {
|
||||||
className = suffixClasses(className, '-add');
|
className = suffixClasses(className, '-add');
|
||||||
} else if(event == 'removeClass') {
|
} else if(event == 'removeClass') {
|
||||||
|
|
@ -504,7 +505,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
|
|
||||||
if(animation.start) {
|
if(animation.start) {
|
||||||
if(event == 'addClass' || event == 'removeClass') {
|
if(event == 'addClass' || event == 'removeClass') {
|
||||||
animation.endFn = animation.start(element, className, fn);
|
animation.endFn = animation.start(element, baseClassName, fn);
|
||||||
} else {
|
} else {
|
||||||
animation.endFn = animation.start(element, fn);
|
animation.endFn = animation.start(element, fn);
|
||||||
}
|
}
|
||||||
|
|
@ -520,17 +521,6 @@ angular.module('ngAnimate', ['ng'])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function suffixClasses(classes, suffix) {
|
|
||||||
var className = '';
|
|
||||||
classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
|
|
||||||
forEach(classes, function(klass, i) {
|
|
||||||
if(klass && klass.length > 0) {
|
|
||||||
className += (i > 0 ? ' ' : '') + klass + suffix;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return className;
|
|
||||||
}
|
|
||||||
|
|
||||||
function progress(index) {
|
function progress(index) {
|
||||||
animations[index].done = true;
|
animations[index].done = true;
|
||||||
(animations[index].endFn || noop)();
|
(animations[index].endFn || noop)();
|
||||||
|
|
@ -550,117 +540,128 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
}])
|
|
||||||
|
|
||||||
.animation('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) {
|
$animateProvider.register('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) {
|
||||||
var noop = angular.noop;
|
var noop = angular.noop;
|
||||||
var forEach = angular.forEach;
|
var forEach = angular.forEach;
|
||||||
function animate(element, className, done) {
|
function animate(element, className, done) {
|
||||||
if (!($sniffer.transitions || $sniffer.animations)) {
|
if (!($sniffer.transitions || $sniffer.animations)) {
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
var activeClassName = '';
|
|
||||||
$timeout(startAnimation, 1, false);
|
|
||||||
|
|
||||||
//this acts as the cancellation function in case
|
|
||||||
//a new animation is triggered while another animation
|
|
||||||
//is still going on (otherwise the active className
|
|
||||||
//would still hang around until the timer is complete).
|
|
||||||
return onEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseMaxTime(str) {
|
|
||||||
var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
|
|
||||||
forEach(values, function(value) {
|
|
||||||
total = Math.max(parseFloat(value) || 0, total);
|
|
||||||
});
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startAnimation() {
|
|
||||||
var duration = 0;
|
|
||||||
forEach(className.split(' '), function(klass, i) {
|
|
||||||
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
|
||||||
});
|
|
||||||
|
|
||||||
element.addClass(activeClassName);
|
|
||||||
|
|
||||||
//one day all browsers will have these properties
|
|
||||||
var w3cAnimationProp = 'animation';
|
|
||||||
var w3cTransitionProp = 'transition';
|
|
||||||
|
|
||||||
//but some still use vendor-prefixed styles
|
|
||||||
var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
|
|
||||||
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
|
|
||||||
|
|
||||||
var durationKey = 'Duration',
|
|
||||||
delayKey = 'Delay',
|
|
||||||
animationIterationCountKey = 'IterationCount';
|
|
||||||
|
|
||||||
//we want all the styles defined before and after
|
|
||||||
var ELEMENT_NODE = 1;
|
|
||||||
forEach(element, function(element) {
|
|
||||||
if (element.nodeType == ELEMENT_NODE) {
|
|
||||||
var elementStyles = $window.getComputedStyle(element) || {};
|
|
||||||
|
|
||||||
var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
|
|
||||||
parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
|
|
||||||
|
|
||||||
var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
|
|
||||||
parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
|
|
||||||
|
|
||||||
var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
|
|
||||||
parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
|
|
||||||
|
|
||||||
var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
|
|
||||||
parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
|
|
||||||
|
|
||||||
if(animationDuration > 0) {
|
|
||||||
animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
|
|
||||||
parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
|
|
||||||
1);
|
|
||||||
}
|
|
||||||
|
|
||||||
duration = Math.max(animationDelay + animationDuration,
|
|
||||||
transitionDelay + transitionDuration,
|
|
||||||
duration);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$timeout(done, duration * 1000, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
//this will automatically be called by $animate so
|
|
||||||
//there is no need to attach this internally to the
|
|
||||||
//timeout done method
|
|
||||||
function onEnd(cancelled) {
|
|
||||||
element.removeClass(activeClassName);
|
|
||||||
|
|
||||||
//only when the animation is cancelled is the done()
|
|
||||||
//function not called for this animation therefore
|
|
||||||
//this must be also called
|
|
||||||
if(cancelled) {
|
|
||||||
done();
|
done();
|
||||||
|
} else {
|
||||||
|
var activeClassName = '';
|
||||||
|
$timeout(startAnimation, 1, false);
|
||||||
|
|
||||||
|
//this acts as the cancellation function in case
|
||||||
|
//a new animation is triggered while another animation
|
||||||
|
//is still going on (otherwise the active className
|
||||||
|
//would still hang around until the timer is complete).
|
||||||
|
return onEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMaxTime(str) {
|
||||||
|
var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
|
||||||
|
forEach(values, function(value) {
|
||||||
|
total = Math.max(parseFloat(value) || 0, total);
|
||||||
|
});
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAnimation() {
|
||||||
|
var duration = 0;
|
||||||
|
forEach(className.split(' '), function(klass, i) {
|
||||||
|
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
||||||
|
});
|
||||||
|
|
||||||
|
element.addClass(activeClassName);
|
||||||
|
|
||||||
|
//one day all browsers will have these properties
|
||||||
|
var w3cAnimationProp = 'animation';
|
||||||
|
var w3cTransitionProp = 'transition';
|
||||||
|
|
||||||
|
//but some still use vendor-prefixed styles
|
||||||
|
var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
|
||||||
|
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
|
||||||
|
|
||||||
|
var durationKey = 'Duration',
|
||||||
|
delayKey = 'Delay',
|
||||||
|
animationIterationCountKey = 'IterationCount';
|
||||||
|
|
||||||
|
//we want all the styles defined before and after
|
||||||
|
var ELEMENT_NODE = 1;
|
||||||
|
forEach(element, function(element) {
|
||||||
|
if (element.nodeType == ELEMENT_NODE) {
|
||||||
|
var elementStyles = $window.getComputedStyle(element) || {};
|
||||||
|
|
||||||
|
var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
|
||||||
|
parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
|
||||||
|
|
||||||
|
var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
|
||||||
|
parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
|
||||||
|
|
||||||
|
var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
|
||||||
|
parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
|
||||||
|
|
||||||
|
var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
|
||||||
|
parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
|
||||||
|
|
||||||
|
if(animationDuration > 0) {
|
||||||
|
animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
|
||||||
|
parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
duration = Math.max(animationDelay + animationDuration,
|
||||||
|
transitionDelay + transitionDuration,
|
||||||
|
duration);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$timeout(done, duration * 1000, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//this will automatically be called by $animate so
|
||||||
|
//there is no need to attach this internally to the
|
||||||
|
//timeout done method
|
||||||
|
function onEnd(cancelled) {
|
||||||
|
element.removeClass(activeClassName);
|
||||||
|
|
||||||
|
//only when the animation is cancelled is the done()
|
||||||
|
//function not called for this animation therefore
|
||||||
|
//this must be also called
|
||||||
|
if(cancelled) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
enter : function(element, done) {
|
||||||
|
return animate(element, 'ng-enter', done);
|
||||||
|
},
|
||||||
|
leave : function(element, done) {
|
||||||
|
return animate(element, 'ng-leave', done);
|
||||||
|
},
|
||||||
|
move : function(element, done) {
|
||||||
|
return animate(element, 'ng-move', done);
|
||||||
|
},
|
||||||
|
addClass : function(element, className, done) {
|
||||||
|
return animate(element, suffixClasses(className, '-add'), done);
|
||||||
|
},
|
||||||
|
removeClass : function(element, className, done) {
|
||||||
|
return animate(element, suffixClasses(className, '-remove'), done);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}]);
|
||||||
|
|
||||||
|
function suffixClasses(classes, suffix) {
|
||||||
|
var className = '';
|
||||||
|
classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
|
||||||
|
forEach(classes, function(klass, i) {
|
||||||
|
if(klass && klass.length > 0) {
|
||||||
|
className += (i > 0 ? ' ' : '') + klass + suffix;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return className;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
enter : function(element, done) {
|
|
||||||
return animate(element, 'ng-enter', done);
|
|
||||||
},
|
|
||||||
leave : function(element, done) {
|
|
||||||
return animate(element, 'ng-leave', done);
|
|
||||||
},
|
|
||||||
move : function(element, done) {
|
|
||||||
return animate(element, 'ng-move', done);
|
|
||||||
},
|
|
||||||
addClass : function(element, className, done) {
|
|
||||||
return animate(element, className, done);
|
|
||||||
},
|
|
||||||
removeClass : function(element, className, done) {
|
|
||||||
return animate(element, className, done);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
|
|
||||||
|
|
@ -1513,4 +1513,36 @@ describe("ngAnimate", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should provide the correct CSS class to the addClass and removeClass callbacks within a JS animation", function() {
|
||||||
|
module(function($animateProvider) {
|
||||||
|
$animateProvider.register('.classify', function($timeout) {
|
||||||
|
return {
|
||||||
|
removeClass : function(element, className, done) {
|
||||||
|
element.data('classify','remove-' + className);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
addClass : function(element, className, done) {
|
||||||
|
element.data('classify','add-' + className);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
inject(function($compile, $rootScope, $animate, $timeout) {
|
||||||
|
var element = html($compile('<div class="classify"></div>')($rootScope));
|
||||||
|
|
||||||
|
$animate.addClass(element, 'super');
|
||||||
|
expect(element.data('classify')).toBe('add-super');
|
||||||
|
$timeout.flush();
|
||||||
|
|
||||||
|
$animate.removeClass(element, 'super');
|
||||||
|
expect(element.data('classify')).toBe('remove-super');
|
||||||
|
$timeout.flush();
|
||||||
|
|
||||||
|
$animate.addClass(element, 'superguy');
|
||||||
|
expect(element.data('classify')).toBe('add-superguy');
|
||||||
|
$timeout.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue