diff --git a/src/ng/animate.js b/src/ng/animate.js
index 569910151314..609ebf946333 100644
--- a/src/ng/animate.js
+++ b/src/ng/animate.js
@@ -122,7 +122,8 @@ var $AnimateProvider = ['$provide', function($provide) {
}
});
- return (toAdd.length + toRemove.length) > 0 && [toAdd.length && toAdd, toRemove.length && toRemove];
+ return (toAdd.length + toRemove.length) > 0 &&
+ [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null];
}
function cachedClassManipulation(cache, classes, op) {
@@ -144,6 +145,13 @@ var $AnimateProvider = ['$provide', function($provide) {
return currentDefer.promise;
}
+ function applyStyles(element, options) {
+ if (angular.isObject(options)) {
+ var styles = extend(options.from || {}, options.to || {});
+ element.css(styles);
+ }
+ }
+
/**
*
* @ngdoc service
@@ -162,6 +170,10 @@ var $AnimateProvider = ['$provide', function($provide) {
* page}.
*/
return {
+ animate : function(element, from, to) {
+ applyStyles(element, { from: from, to: to });
+ return asyncPromise();
+ },
/**
*
@@ -176,9 +188,11 @@ var $AnimateProvider = ['$provide', function($provide) {
* a child (if the after element is not present)
* @param {DOMElement} after the sibling element which will append the element
* after itself
+ * @param {object=} options an optional collection of styles that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- enter : function(element, parent, after) {
+ enter : function(element, parent, after, options) {
+ applyStyles(element, options);
after ? after.after(element)
: parent.prepend(element);
return asyncPromise();
@@ -192,9 +206,10 @@ var $AnimateProvider = ['$provide', function($provide) {
* @description Removes the element from the DOM. When the function is called a promise
* is returned that will be resolved at a later time.
* @param {DOMElement} element the element which will be removed from the DOM
+ * @param {object=} options an optional collection of options that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- leave : function(element) {
+ leave : function(element, options) {
element.remove();
return asyncPromise();
},
@@ -214,12 +229,13 @@ var $AnimateProvider = ['$provide', function($provide) {
* inserted into (if the after element is not present)
* @param {DOMElement} after the sibling element where the element will be
* positioned next to
+ * @param {object=} options an optional collection of options that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- move : function(element, parent, after) {
+ move : function(element, parent, after, options) {
// Do not remove element before insert. Removing will cause data associated with the
// element to be dropped. Insert will implicitly do the remove.
- return this.enter(element, parent, after);
+ return this.enter(element, parent, after, options);
},
/**
@@ -232,13 +248,14 @@ var $AnimateProvider = ['$provide', function($provide) {
* @param {DOMElement} element the element which will have the className value
* added to it
* @param {string} className the CSS class which will be added to the element
+ * @param {object=} options an optional collection of options that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- addClass : function(element, className) {
- return this.setClass(element, className, []);
+ addClass : function(element, className, options) {
+ return this.setClass(element, className, [], options);
},
- $$addClassImmediately : function(element, className) {
+ $$addClassImmediately : function(element, className, options) {
element = jqLite(element);
className = !isString(className)
? (isArray(className) ? className.join(' ') : '')
@@ -246,6 +263,8 @@ var $AnimateProvider = ['$provide', function($provide) {
forEach(element, function (element) {
jqLiteAddClass(element, className);
});
+ applyStyles(element, options);
+ return asyncPromise();
},
/**
@@ -258,13 +277,14 @@ var $AnimateProvider = ['$provide', function($provide) {
* @param {DOMElement} element the element which will have the className value
* removed from it
* @param {string} className the CSS class which will be removed from the element
+ * @param {object=} options an optional collection of options that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- removeClass : function(element, className) {
- return this.setClass(element, [], className);
+ removeClass : function(element, className, options) {
+ return this.setClass(element, [], className, options);
},
- $$removeClassImmediately : function(element, className) {
+ $$removeClassImmediately : function(element, className, options) {
element = jqLite(element);
className = !isString(className)
? (isArray(className) ? className.join(' ') : '')
@@ -272,6 +292,7 @@ var $AnimateProvider = ['$provide', function($provide) {
forEach(element, function (element) {
jqLiteRemoveClass(element, className);
});
+ applyStyles(element, options);
return asyncPromise();
},
@@ -286,9 +307,10 @@ var $AnimateProvider = ['$provide', function($provide) {
* removed from it
* @param {string} add the CSS classes which will be added to the element
* @param {string} remove the CSS class which will be removed from the element
+ * @param {object=} options an optional collection of options that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- setClass : function(element, add, remove) {
+ setClass : function(element, add, remove, options) {
var self = this;
var STORAGE_KEY = '$$animateClasses';
var createdCache = false;
@@ -297,9 +319,12 @@ var $AnimateProvider = ['$provide', function($provide) {
var cache = element.data(STORAGE_KEY);
if (!cache) {
cache = {
- classes: {}
+ classes: {},
+ options : options
};
createdCache = true;
+ } else if (options && cache.options) {
+ cache.options = angular.extend(cache.options || {}, options);
}
var classes = cache.classes;
@@ -320,7 +345,7 @@ var $AnimateProvider = ['$provide', function($provide) {
if (cache) {
var classes = resolveElementClasses(element, cache.classes);
if (classes) {
- self.$$setClassImmediately(element, classes[0], classes[1]);
+ self.$$setClassImmediately(element, classes[0], classes[1], cache.options);
}
}
@@ -332,9 +357,10 @@ var $AnimateProvider = ['$provide', function($provide) {
return cache.promise;
},
- $$setClassImmediately : function(element, add, remove) {
+ $$setClassImmediately : function(element, add, remove, options) {
add && this.$$addClassImmediately(element, add);
remove && this.$$removeClassImmediately(element, remove);
+ applyStyles(element, options);
return asyncPromise();
},
diff --git a/src/ng/directive/ngShowHide.js b/src/ng/directive/ngShowHide.js
index ebcc05b14111..414b986a9ae6 100644
--- a/src/ng/directive/ngShowHide.js
+++ b/src/ng/directive/ngShowHide.js
@@ -167,7 +167,9 @@ var ngShowDirective = ['$animate', function($animate) {
// we can control when the element is actually displayed on screen without having
// to have a global/greedy CSS selector that breaks when other animations are run.
// Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
- $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, NG_HIDE_IN_PROGRESS_CLASS);
+ $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
+ tempClasses : NG_HIDE_IN_PROGRESS_CLASS
+ });
});
}
};
@@ -324,7 +326,9 @@ var ngHideDirective = ['$animate', function($animate) {
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
// The comment inside of the ngShowDirective explains why we add and
// remove a temporary class for the show/hide animation
- $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, NG_HIDE_IN_PROGRESS_CLASS);
+ $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
+ tempClasses : NG_HIDE_IN_PROGRESS_CLASS
+ });
});
}
};
diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js
index a754408992ec..cecaf6fd1bca 100644
--- a/src/ngAnimate/animate.js
+++ b/src/ngAnimate/animate.js
@@ -83,7 +83,7 @@
* will automatically extend the wait time to enable animations once **all** of the outbound HTTP requests
* are complete.
*
- *
CSS-defined Animations
+ * ## CSS-defined Animations
* The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
* are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported
* and can be used to play along with this naming structure.
@@ -320,6 +320,49 @@
* and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation
* or transition code that is defined via a stylesheet).
*
+ *
+ * ### Applying Directive-specific Styles to an Animation
+ * In some cases a directive or service may want to provide `$animate` with extra details that the animation will
+ * include into its animation. Let's say for example we wanted to render an animation that animates an element
+ * towards the mouse coordinates as to where the user clicked last. By collecting the X/Y coordinates of the click
+ * (via the event parameter) we can set the `top` and `left` styles into an object and pass that into our function
+ * call to `$animate.addClass`.
+ *
+ * ```js
+ * canvas.on('click', function(e) {
+ * $animate.addClass(element, 'on', {
+ * to: {
+ * left : e.client.x + 'px',
+ * top : e.client.y + 'px'
+ * }
+ * }):
+ * });
+ * ```
+ *
+ * Now when the animation runs, and a transition or keyframe animation is picked up, then the animation itself will
+ * also include and transition the styling of the `left` and `top` properties into its running animation. If we want
+ * to provide some starting animation values then we can do so by placing the starting animations styles into an object
+ * called `from` in the same object as the `to` animations.
+ *
+ * ```js
+ * canvas.on('click', function(e) {
+ * $animate.addClass(element, 'on', {
+ * from: {
+ * position: 'absolute',
+ * left: '0px',
+ * top: '0px'
+ * },
+ * to: {
+ * left : e.client.x + 'px',
+ * top : e.client.y + 'px'
+ * }
+ * }):
+ * });
+ * ```
+ *
+ * Once the animation is complete or cancelled then the union of both the before and after styles are applied to the
+ * element. If `ngAnimate` is not present then the styles will be applied immediately.
+ *
*/
angular.module('ngAnimate', ['ng'])
@@ -378,6 +421,7 @@ angular.module('ngAnimate', ['ng'])
var selectors = $animateProvider.$$selectors;
var isArray = angular.isArray;
var isString = angular.isString;
+ var isObject = angular.isObject;
var ELEMENT_NODE = 1;
var NG_ANIMATE_STATE = '$$ngAnimateState';
@@ -472,8 +516,12 @@ angular.module('ngAnimate', ['ng'])
// some plugin code may still be passing in the callback
// function as the last param for the $animate methods so
// it's best to only allow string or array values for now
- if (isArray(options)) return options;
- if (isString(options)) return [options];
+ if (isObject(options)) {
+ if (options.tempClasses && isString(options.tempClasses)) {
+ options.tempClasses = options.tempClasses.split(/\s+/);
+ }
+ return options;
+ }
}
function resolveElementClasses(element, cache, runningAnimations) {
@@ -550,7 +598,7 @@ angular.module('ngAnimate', ['ng'])
}
}
- function animationRunner(element, animationEvent, className) {
+ function animationRunner(element, animationEvent, className, options) {
//transcluded directives may sometimes fire an animation using only comment nodes
//best to catch this early on to prevent any animation operations from occurring
var node = element[0];
@@ -558,6 +606,11 @@ angular.module('ngAnimate', ['ng'])
return;
}
+ if (options) {
+ options.to = options.to || {};
+ options.from = options.from || {};
+ }
+
var classNameAdd;
var classNameRemove;
if (isArray(className)) {
@@ -575,9 +628,10 @@ angular.module('ngAnimate', ['ng'])
}
var isSetClassOperation = animationEvent == 'setClass';
- var isClassBased = isSetClassOperation ||
- animationEvent == 'addClass' ||
- animationEvent == 'removeClass';
+ var isClassBased = isSetClassOperation
+ || animationEvent == 'addClass'
+ || animationEvent == 'removeClass'
+ || animationEvent == 'animate';
var currentClassName = element.attr('class');
var classes = currentClassName + ' ' + className;
@@ -645,16 +699,19 @@ angular.module('ngAnimate', ['ng'])
};
switch(animation.event) {
case 'setClass':
- cancellations.push(animation.fn(element, classNameAdd, classNameRemove, progress));
+ cancellations.push(animation.fn(element, classNameAdd, classNameRemove, progress, options));
+ break;
+ case 'animate':
+ cancellations.push(animation.fn(element, className, options.from, options.to, progress));
break;
case 'addClass':
- cancellations.push(animation.fn(element, classNameAdd || className, progress));
+ cancellations.push(animation.fn(element, classNameAdd || className, progress, options));
break;
case 'removeClass':
- cancellations.push(animation.fn(element, classNameRemove || className, progress));
+ cancellations.push(animation.fn(element, classNameRemove || className, progress, options));
break;
default:
- cancellations.push(animation.fn(element, progress));
+ cancellations.push(animation.fn(element, progress, options));
break;
}
});
@@ -670,6 +727,11 @@ angular.module('ngAnimate', ['ng'])
className : className,
isClassBased : isClassBased,
isSetClassOperation : isSetClassOperation,
+ applyStyles : function() {
+ if (options) {
+ element.css(angular.extend(options.from || {}, options.to || {}));
+ }
+ },
before : function(allCompleteFn) {
beforeComplete = allCompleteFn;
run(before, beforeCancel, function() {
@@ -761,6 +823,65 @@ angular.module('ngAnimate', ['ng'])
*
*/
return {
+ /**
+ * @ngdoc method
+ * @name $animate#animate
+ * @kind function
+ *
+ * @description
+ * Performs an inline animation on the element which applies the provided `to` and `from` CSS styles to the element.
+ * If any detected CSS transition, keyframe or JavaScript matches the provided `className` value then the animation
+ * will take on the provided styles. For example, if a transition animation is set for the given className then the
+ * provided `from` and `to` styles will be applied alongside the given transition. If a JavaScript animation is
+ * detected then the provided styles will be given in as function paramters.
+ *
+ * ```js
+ * ngModule.animation('.my-inline-animation', function() {
+ * return {
+ * animate : function(element, className, from, to, done) {
+ * //styles
+ * }
+ * }
+ * });
+ * ```
+ *
+ * Below is a breakdown of each step that occurs during the `animate` animation:
+ *
+ * | Animation Step | What the element class attribute looks like |
+ * |-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|
+ * | 1. $animate.animate(...) is called | class="my-animation" |
+ * | 2. $animate waits for the next digest to start the animation | class="my-animation ng-animate" |
+ * | 3. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate" |
+ * | 4. the className class value is added to the element | class="my-animation ng-animate className" |
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate className" |
+ * | 6. $animate blocks all CSS transitions on the element to ensure the .className class styling is applied right away| class="my-animation ng-animate className" |
+ * | 7. $animate applies the provided collection of `from` CSS styles to the element | class="my-animation ng-animate className" |
+ * | 8. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate className" |
+ * | 9. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate className" |
+ * | 10. the className-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate className className-active" |
+ * | 11. $animate applies the collection of `to` CSS styles to the element which are then handled by the transition | class="my-animation ng-animate className className-active" |
+ * | 12. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate className className-active" |
+ * | 13. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
+ * | 14. The returned promise is resolved. | class="my-animation" |
+ *
+ * @param {DOMElement} element the element that will be the focus of the enter animation
+ * @param {object} from a collection of CSS styles that will be applied to the element at the start of the animation
+ * @param {object} to a collection of CSS styles that the element will animate towards
+ * @param {string=} className an optional CSS class that will be added to the element for the duration of the animation (the default class is `ng-inline-animate`)
+ * @param {object=} options an optional collection of options that will be picked up by the CSS transition/animation
+ * @return {Promise} the animation callback promise
+ */
+ animate : function(element, from, to, className, options) {
+ className = className || 'ng-inline-animate';
+ options = parseAnimateOptions(options) || {};
+ options.from = to ? from : null;
+ options.to = to ? to : from;
+
+ return runAnimationPostDigest(function(done) {
+ return performAnimation('animate', className, stripCommentsFromElement(element), null, null, noop, options, done);
+ });
+ },
+
/**
* @ngdoc method
* @name $animate#enter
@@ -791,6 +912,7 @@ angular.module('ngAnimate', ['ng'])
* @param {DOMElement} element the element that will be the focus of the enter animation
* @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation
* @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation
+ * @param {object=} options an optional collection of options that will be picked up by the CSS transition/animation
* @return {Promise} the animation callback promise
*/
enter : function(element, parentElement, afterElement, options) {
@@ -834,6 +956,7 @@ angular.module('ngAnimate', ['ng'])
* | 13. The returned promise is resolved. | ... |
*
* @param {DOMElement} element the element that will be the focus of the leave animation
+ * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation
* @return {Promise} the animation callback promise
*/
leave : function(element, options) {
@@ -880,6 +1003,7 @@ angular.module('ngAnimate', ['ng'])
* @param {DOMElement} element the element that will be the focus of the move animation
* @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation
* @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation
+ * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation
* @return {Promise} the animation callback promise
*/
move : function(element, parentElement, afterElement, options) {
@@ -923,6 +1047,7 @@ angular.module('ngAnimate', ['ng'])
*
* @param {DOMElement} element the element that will be animated
* @param {string} className the CSS class that will be added to the element and then animated
+ * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation
* @return {Promise} the animation callback promise
*/
addClass : function(element, className, options) {
@@ -956,6 +1081,7 @@ angular.module('ngAnimate', ['ng'])
*
* @param {DOMElement} element the element that will be animated
* @param {string} className the CSS class that will be animated and then removed from the element
+ * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation
* @return {Promise} the animation callback promise
*/
removeClass : function(element, className, options) {
@@ -987,6 +1113,7 @@ angular.module('ngAnimate', ['ng'])
* @param {string} add the CSS classes which will be added to the element
* @param {string} remove the CSS class which will be removed from the element
* CSS classes have been set on the element
+ * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation
* @return {Promise} the animation callback promise
*/
setClass : function(element, add, remove, options) {
@@ -997,7 +1124,7 @@ angular.module('ngAnimate', ['ng'])
element = stripCommentsFromElement(element);
if (classBasedAnimationsBlocked(element)) {
- return $delegate.$$setClassImmediately(element, add, remove);
+ return $delegate.$$setClassImmediately(element, add, remove, options);
}
// we're using a combined array for both the add and remove
@@ -1026,7 +1153,7 @@ angular.module('ngAnimate', ['ng'])
if (hasCache) {
if (options && cache.options) {
- cache.options = cache.options.concat(options);
+ cache.options = angular.extend(cache.options || {}, options);
}
//the digest cycle will combine all the animations into one function
@@ -1121,9 +1248,8 @@ angular.module('ngAnimate', ['ng'])
and the onComplete callback will be fired once the animation is fully complete.
*/
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, options, doneCallback) {
-
var noopCancel = noop;
- var runner = animationRunner(element, animationEvent, className);
+ var runner = animationRunner(element, animationEvent, className, options);
if (!runner) {
fireDOMOperation();
fireBeforeCallbackAsync();
@@ -1193,7 +1319,10 @@ angular.module('ngAnimate', ['ng'])
}
}
- if (runner.isClassBased && !runner.isSetClassOperation && !skipAnimation) {
+ if (runner.isClassBased
+ && !runner.isSetClassOperation
+ && animationEvent != 'animate'
+ && !skipAnimation) {
skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR
}
@@ -1228,8 +1357,8 @@ angular.module('ngAnimate', ['ng'])
//the ng-animate class does nothing, but it's here to allow for
//parent animations to find and cancel child animations when needed
element.addClass(NG_ANIMATE_CLASS_NAME);
- if (isArray(options)) {
- forEach(options, function(className) {
+ if (options && options.tempClasses) {
+ forEach(options.tempClasses, function(className) {
element.addClass(className);
});
}
@@ -1301,9 +1430,13 @@ angular.module('ngAnimate', ['ng'])
function closeAnimation() {
if (!closeAnimation.hasBeenRun) {
+ if (runner) { //the runner doesn't exist if it fails to instantiate
+ runner.applyStyles();
+ }
+
closeAnimation.hasBeenRun = true;
- if (isArray(options)) {
- forEach(options, function(className) {
+ if (options && options.tempClasses) {
+ forEach(options.tempClasses, function(className) {
element.removeClass(className);
});
}
@@ -1594,7 +1727,7 @@ angular.module('ngAnimate', ['ng'])
return parentID + '-' + extractElementNode(element).getAttribute('class');
}
- function animateSetup(animationEvent, element, className) {
+ function animateSetup(animationEvent, element, className, styles) {
var structural = ['ng-enter','ng-leave','ng-move'].indexOf(className) >= 0;
var cacheKey = getCacheKey(element);
@@ -1626,7 +1759,7 @@ angular.module('ngAnimate', ['ng'])
return false;
}
- var blockTransition = structural && transitionDuration > 0;
+ var blockTransition = styles || (structural && transitionDuration > 0);
var blockAnimation = animationDuration > 0 &&
stagger.animationDelay > 0 &&
stagger.animationDuration === 0;
@@ -1645,6 +1778,9 @@ angular.module('ngAnimate', ['ng'])
if (blockTransition) {
blockTransitions(node, true);
+ if (styles) {
+ element.css(styles);
+ }
}
if (blockAnimation) {
@@ -1654,7 +1790,7 @@ angular.module('ngAnimate', ['ng'])
return true;
}
- function animateRun(animationEvent, element, className, activeAnimationComplete) {
+ function animateRun(animationEvent, element, className, activeAnimationComplete, styles) {
var node = extractElementNode(element);
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
if (node.getAttribute('class').indexOf(className) == -1 || !elementData) {
@@ -1662,10 +1798,6 @@ angular.module('ngAnimate', ['ng'])
return;
}
- if (elementData.blockTransition) {
- blockTransitions(node, false);
- }
-
var activeClassName = '';
var pendingClassName = '';
forEach(className.split(' '), function(klass, i) {
@@ -1696,6 +1828,9 @@ angular.module('ngAnimate', ['ng'])
if (!staggerTime) {
element.addClass(activeClassName);
+ if (elementData.blockTransition) {
+ blockTransitions(node, false);
+ }
}
var eventCacheKey = elementData.cacheKey + ' ' + activeClassName;
@@ -1708,6 +1843,14 @@ angular.module('ngAnimate', ['ng'])
return;
}
+ if (!staggerTime && styles) {
+ if (!timings.transitionDuration) {
+ element.css('transition', timings.animationDuration + 's linear all');
+ appliedStyles.push('transition');
+ }
+ element.css(styles);
+ }
+
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
var maxDelayTime = maxDelay * ONE_SECOND;
@@ -1732,11 +1875,24 @@ angular.module('ngAnimate', ['ng'])
element.addClass(pendingClassName);
staggerTimeout = $timeout(function() {
staggerTimeout = null;
- element.addClass(activeClassName);
- element.removeClass(pendingClassName);
+
+ if (timings.transitionDuration > 0) {
+ blockTransitions(node, false);
+ }
if (timings.animationDuration > 0) {
blockAnimations(node, false);
}
+
+ element.addClass(activeClassName);
+ element.removeClass(pendingClassName);
+
+ if (styles) {
+ if (timings.transitionDuration === 0) {
+ element.css('transition', timings.animationDuration + 's linear all');
+ }
+ element.css(styles);
+ appliedStyles.push('transition');
+ }
}, staggerTime * ONE_SECOND, false);
}
@@ -1797,28 +1953,28 @@ angular.module('ngAnimate', ['ng'])
node.style[ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY] = bool ? 'paused' : '';
}
- function animateBefore(animationEvent, element, className, calculationDecorator) {
- if (animateSetup(animationEvent, element, className, calculationDecorator)) {
+ function animateBefore(animationEvent, element, className, styles) {
+ if (animateSetup(animationEvent, element, className, styles)) {
return function(cancelled) {
cancelled && animateClose(element, className);
};
}
}
- function animateAfter(animationEvent, element, className, afterAnimationComplete) {
+ function animateAfter(animationEvent, element, className, afterAnimationComplete, styles) {
if (element.data(NG_ANIMATE_CSS_DATA_KEY)) {
- return animateRun(animationEvent, element, className, afterAnimationComplete);
+ return animateRun(animationEvent, element, className, afterAnimationComplete, styles);
} else {
animateClose(element, className);
afterAnimationComplete();
}
}
- function animate(animationEvent, element, className, animationComplete) {
+ function animate(animationEvent, element, className, animationComplete, options) {
//If the animateSetup function doesn't bother returning a
//cancellation function then it means that there is no animation
//to perform at all
- var preReflowCancellation = animateBefore(animationEvent, element, className);
+ var preReflowCancellation = animateBefore(animationEvent, element, className, options.from);
if (!preReflowCancellation) {
clearCacheAfterReflow();
animationComplete();
@@ -1835,7 +1991,7 @@ angular.module('ngAnimate', ['ng'])
//once the reflow is complete then we point cancel to
//the new cancellation function which will remove all of the
//animation properties from the active animation
- cancel = animateAfter(animationEvent, element, className, animationComplete);
+ cancel = animateAfter(animationEvent, element, className, animationComplete, options.to);
});
return function(cancelled) {
@@ -1857,22 +2013,33 @@ angular.module('ngAnimate', ['ng'])
}
return {
- enter : function(element, animationCompleted) {
- return animate('enter', element, 'ng-enter', animationCompleted);
+ animate : function(element, className, from, to, animationCompleted, options) {
+ options = options || {};
+ options.from = from;
+ options.to = to;
+ return animate('animate', element, className, animationCompleted, options);
+ },
+
+ enter : function(element, animationCompleted, options) {
+ options = options || {};
+ return animate('enter', element, 'ng-enter', animationCompleted, options);
},
- leave : function(element, animationCompleted) {
- return animate('leave', element, 'ng-leave', animationCompleted);
+ leave : function(element, animationCompleted, options) {
+ options = options || {};
+ return animate('leave', element, 'ng-leave', animationCompleted, options);
},
- move : function(element, animationCompleted) {
- return animate('move', element, 'ng-move', animationCompleted);
+ move : function(element, animationCompleted, options) {
+ options = options || {};
+ return animate('move', element, 'ng-move', animationCompleted, options);
},
- beforeSetClass : function(element, add, remove, animationCompleted) {
+ beforeSetClass : function(element, add, remove, animationCompleted, options) {
+ options = options || {};
var className = suffixClasses(remove, '-remove') + ' ' +
suffixClasses(add, '-add');
- var cancellationMethod = animateBefore('setClass', element, className);
+ var cancellationMethod = animateBefore('setClass', element, className, options.from);
if (cancellationMethod) {
afterReflow(element, animationCompleted);
return cancellationMethod;
@@ -1881,8 +2048,9 @@ angular.module('ngAnimate', ['ng'])
animationCompleted();
},
- beforeAddClass : function(element, className, animationCompleted) {
- var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'));
+ beforeAddClass : function(element, className, animationCompleted, options) {
+ options = options || {};
+ var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'), options.from);
if (cancellationMethod) {
afterReflow(element, animationCompleted);
return cancellationMethod;
@@ -1891,8 +2059,9 @@ angular.module('ngAnimate', ['ng'])
animationCompleted();
},
- beforeRemoveClass : function(element, className, animationCompleted) {
- var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'));
+ beforeRemoveClass : function(element, className, animationCompleted, options) {
+ options = options || {};
+ var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'), options.from);
if (cancellationMethod) {
afterReflow(element, animationCompleted);
return cancellationMethod;
@@ -1901,19 +2070,22 @@ angular.module('ngAnimate', ['ng'])
animationCompleted();
},
- setClass : function(element, add, remove, animationCompleted) {
+ setClass : function(element, add, remove, animationCompleted, options) {
+ options = options || {};
remove = suffixClasses(remove, '-remove');
add = suffixClasses(add, '-add');
var className = remove + ' ' + add;
- return animateAfter('setClass', element, className, animationCompleted);
+ return animateAfter('setClass', element, className, animationCompleted, options.to);
},
- addClass : function(element, className, animationCompleted) {
- return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted);
+ addClass : function(element, className, animationCompleted, options) {
+ options = options || {};
+ return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted, options.to);
},
- removeClass : function(element, className, animationCompleted) {
- return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted);
+ removeClass : function(element, className, animationCompleted, options) {
+ options = options || {};
+ return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted, options.to);
}
};
diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js
index 9690b46391c0..773d20d1d442 100644
--- a/src/ngMock/angular-mocks.js
+++ b/src/ngMock/angular-mocks.js
@@ -803,7 +803,7 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
};
angular.forEach(
- ['enter','leave','move','addClass','removeClass','setClass'], function(method) {
+ ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
animate[method] = function() {
animate.queue.push({
event : method,
diff --git a/test/ng/animateSpec.js b/test/ng/animateSpec.js
index ff1f15cbed8f..f462b6c86b79 100644
--- a/test/ng/animateSpec.js
+++ b/test/ng/animateSpec.js
@@ -50,6 +50,15 @@ describe("$animate", function() {
expect(element.text()).toBe('21');
}));
+ it("should apply styles instantly to the element",
+ inject(function($animate, $compile, $rootScope) {
+
+ $animate.animate(element, { color: 'rgb(0, 0, 0)' });
+ expect(element.css('color')).toBe('rgb(0, 0, 0)');
+
+ $animate.animate(element, { color: 'rgb(255, 0, 0)' }, { color: 'rgb(0, 255, 0)' });
+ expect(element.css('color')).toBe('rgb(0, 255, 0)');
+ }));
it("should still perform DOM operations even if animations are disabled (post-digest)", inject(function($animate, $rootScope) {
$animate.enabled(false);
@@ -103,6 +112,68 @@ describe("$animate", function() {
});
inject();
});
+
+ it("should apply and retain inline styles on the element that is animated", inject(function($animate, $rootScope) {
+ var element = jqLite('');
+ var parent = jqLite('');
+ var other = jqLite('');
+ parent.append(other);
+ $animate.enabled(true);
+
+ $animate.enter(element, parent, null, {
+ to: { color : 'red' }
+ });
+ assertColor('red');
+
+ $animate.move(element, null, other, {
+ to: { color : 'yellow' }
+ });
+ assertColor('yellow');
+
+ $animate.addClass(element, 'on', {
+ to: { color : 'green' }
+ });
+ $rootScope.$digest();
+ assertColor('green');
+
+ $animate.setClass(element, 'off', 'on', {
+ to: { color : 'black' }
+ });
+ $rootScope.$digest();
+ assertColor('black');
+
+ $animate.removeClass(element, 'off', {
+ to: { color : 'blue' }
+ });
+ $rootScope.$digest();
+ assertColor('blue');
+
+ $animate.leave(element, 'off', {
+ to: { color : 'blue' }
+ });
+ assertColor('blue'); //nothing should happen the element is gone anyway
+
+ function assertColor(color) {
+ expect(element[0].style.color).toBe(color);
+ }
+ }));
+
+ it("should merge the from and to styles that are provided",
+ inject(function($animate, $rootScope) {
+
+ var element = jqLite('');
+
+ element.css('color', 'red');
+ $animate.addClass(element, 'on', {
+ from : { color : 'green' },
+ to : { borderColor : 'purple' }
+ });
+ $rootScope.$digest();
+
+ var style = element[0].style;
+ expect(style.color).toBe('green');
+ expect(style.borderColor).toBe('purple');
+ }));
});
describe('CSS class DOM manipulation', function() {
diff --git a/test/ng/directive/ngShowHideSpec.js b/test/ng/directive/ngShowHideSpec.js
index 5140df4fbef1..260ba914e44b 100644
--- a/test/ng/directive/ngShowHideSpec.js
+++ b/test/ng/directive/ngShowHideSpec.js
@@ -170,13 +170,13 @@ describe('ngShow / ngHide animations', function() {
item = $animate.queue.shift();
expect(item.event).toEqual('addClass');
- expect(item.options).toEqual('ng-hide-animate');
+ expect(item.options.tempClasses).toEqual('ng-hide-animate');
$scope.on = true;
$scope.$digest();
item = $animate.queue.shift();
expect(item.event).toEqual('removeClass');
- expect(item.options).toEqual('ng-hide-animate');
+ expect(item.options.tempClasses).toEqual('ng-hide-animate');
}));
});
@@ -217,13 +217,13 @@ describe('ngShow / ngHide animations', function() {
item = $animate.queue.shift();
expect(item.event).toEqual('removeClass');
- expect(item.options).toEqual('ng-hide-animate');
+ expect(item.options.tempClasses).toEqual('ng-hide-animate');
$scope.on = true;
$scope.$digest();
item = $animate.queue.shift();
expect(item.event).toEqual('addClass');
- expect(item.options).toEqual('ng-hide-animate');
+ expect(item.options.tempClasses).toEqual('ng-hide-animate');
}));
});
});
diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js
index a88f4b773ae9..df5b48f1bf13 100644
--- a/test/ngAnimate/animateSpec.js
+++ b/test/ngAnimate/animateSpec.js
@@ -292,7 +292,7 @@ describe("ngAnimate", function() {
});
$animateProvider.register('.custom-delay', function($timeout) {
function animate(element, done) {
- done = arguments.length == 3 ? arguments[2] : done;
+ done = arguments.length == 4 ? arguments[2] : done;
$timeout(done, 2000, false);
return function() {
element.addClass('animation-cancelled');
@@ -306,7 +306,7 @@ describe("ngAnimate", function() {
});
$animateProvider.register('.custom-long-delay', function($timeout) {
function animate(element, done) {
- done = arguments.length == 3 ? arguments[2] : done;
+ done = arguments.length == 4 ? arguments[2] : done;
$timeout(done, 20000, false);
return function(cancelled) {
element.addClass(cancelled ? 'animation-cancelled' : 'animation-ended');
@@ -329,7 +329,7 @@ describe("ngAnimate", function() {
return function($animate, $compile, $rootScope, $rootElement) {
element = $compile('')($rootScope);
- forEach(['.ng-hide-add', '.ng-hide-remove', '.ng-enter', '.ng-leave', '.ng-move'], function(selector) {
+ forEach(['.ng-hide-add', '.ng-hide-remove', '.ng-enter', '.ng-leave', '.ng-move', '.my-inline-animation'], function(selector) {
ss.addRule(selector, '-webkit-transition:1s linear all;' +
'transition:1s linear all;');
});
@@ -454,6 +454,20 @@ describe("ngAnimate", function() {
expect(element.text()).toBe('21');
}));
+ it("should perform the animate event",
+ inject(function($animate, $compile, $rootScope, $timeout, $sniffer) {
+
+ $rootScope.$digest();
+ $animate.animate(element, { color: 'rgb(255, 0, 0)' }, { color: 'rgb(0, 0, 255)' }, 'animated');
+ $rootScope.$digest();
+
+ if($sniffer.transitions) {
+ expect(element.css('color')).toBe('rgb(255, 0, 0)');
+ $animate.triggerReflow();
+ }
+ expect(element.css('color')).toBe('rgb(0, 0, 255)');
+ }));
+
it("should animate the show animation event",
inject(function($animate, $rootScope, $sniffer) {
@@ -653,6 +667,16 @@ describe("ngAnimate", function() {
expect(child.attr('class')).toContain('ng-hide-remove-active');
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
+ //animate
+ $animate.animate(child, null, null, 'my-inline-animation');
+ $rootScope.$digest();
+ $animate.triggerReflow();
+
+ expect(child.attr('class')).toContain('my-inline-animation');
+ expect(child.attr('class')).toContain('my-inline-animation-active');
+ browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
+ $animate.triggerCallbackPromise();
+
//leave
$animate.leave(child);
$rootScope.$digest();
@@ -1037,8 +1061,124 @@ describe("ngAnimate", function() {
expect(element.hasClass('custom-long-delay-add')).toBe(false);
expect(element.hasClass('custom-long-delay-add-active')).toBe(false);
}));
+
+ it('should apply directive styles and provide the style collection to the animation function', function() {
+ var animationDone;
+ var animationStyles;
+ var proxyAnimation = function() {
+ var limit = arguments.length-1;
+ animationStyles = arguments[limit];
+ animationDone = arguments[limit-1];
+ };
+ module(function($animateProvider) {
+ $animateProvider.register('.capture', function() {
+ return {
+ enter : proxyAnimation,
+ leave : proxyAnimation,
+ move : proxyAnimation,
+ addClass : proxyAnimation,
+ removeClass : proxyAnimation,
+ setClass : proxyAnimation
+ };
+ });
+ });
+ inject(function($animate, $rootScope, $compile, $sniffer, $timeout, _$rootElement_) {
+ $rootElement = _$rootElement_;
+
+ $animate.enabled(true);
+
+ element = $compile(html(''))($rootScope);
+ var otherParent = $compile('')($rootScope);
+ var child = $compile('')($rootScope);
+
+ $rootElement.append(otherParent);
+ $rootScope.$digest();
+
+ var styles = {
+ from: { backgroundColor: 'blue' },
+ to: { backgroundColor: 'red' }
+ };
+
+ //enter
+ $animate.enter(child, element, null, styles);
+ $rootScope.$digest();
+ $animate.triggerReflow();
+ expect(animationStyles).toEqual(styles);
+ animationDone();
+ animationDone = animationStyles = null;
+ $animate.triggerCallbacks();
+
+ //move
+ $animate.move(child, null, otherParent, styles);
+ $rootScope.$digest();
+ $animate.triggerReflow();
+ expect(animationStyles).toEqual(styles);
+ animationDone();
+ animationDone = animationStyles = null;
+ $animate.triggerCallbacks();
+
+ //addClass
+ $animate.addClass(child, 'on', styles);
+ $rootScope.$digest();
+ $animate.triggerReflow();
+ expect(animationStyles).toEqual(styles);
+ animationDone();
+ animationDone = animationStyles = null;
+ $animate.triggerCallbacks();
+
+ //setClass
+ $animate.setClass(child, 'off', 'on', styles);
+ $rootScope.$digest();
+ $animate.triggerReflow();
+ expect(animationStyles).toEqual(styles);
+ animationDone();
+ animationDone = animationStyles = null;
+ $animate.triggerCallbacks();
+
+ //removeClass
+ $animate.removeClass(child, 'off', styles);
+ $rootScope.$digest();
+ $animate.triggerReflow();
+ expect(animationStyles).toEqual(styles);
+ animationDone();
+ animationDone = animationStyles = null;
+ $animate.triggerCallbacks();
+
+ //leave
+ $animate.leave(child, styles);
+ $rootScope.$digest();
+ $animate.triggerReflow();
+ expect(animationStyles).toEqual(styles);
+ animationDone();
+ animationDone = animationStyles = null;
+ $animate.triggerCallbacks();
+
+ dealoc(otherParent);
+ });
+ });
});
+ it("should apply animated styles even if there are no detected animations",
+ inject(function($compile, $animate, $rootScope, $sniffer, $rootElement, $document) {
+
+ $animate.enabled(true);
+ jqLite($document[0].body).append($rootElement);
+
+ element = $compile('')($rootScope);
+
+ $animate.enter(element, $rootElement, null, {
+ to : {borderColor: 'red'}
+ });
+
+ $rootScope.$digest();
+ expect(element).toHaveClass('ng-animate');
+
+ $animate.triggerReflow();
+ $animate.triggerCallbacks();
+
+ expect(element).not.toHaveClass('ng-animate');
+ expect(element.attr('style')).toMatch(/border-color: red/);
+ }));
describe("with CSS3", function() {
@@ -1218,6 +1358,28 @@ describe("ngAnimate", function() {
})
);
+ it("should piggy-back-transition the styles with the max keyframe duration if provided by the directive",
+ inject(function($compile, $animate, $rootScope, $sniffer) {
+
+ $animate.enabled(true);
+ ss.addRule('.on', '-webkit-animation: 1s keyframeanimation; animation: 1s keyframeanimation;');
+
+ element = $compile(html('1
'))($rootScope);
+
+ $animate.addClass(element, 'on', {
+ to: {borderColor: 'blue'}
+ });
+
+ $rootScope.$digest();
+ if ($sniffer.transitions) {
+ $animate.triggerReflow();
+ expect(element.attr('style')).toContain('border-color: blue');
+ expect(element.attr('style')).toMatch(/transition:.*1s/);
+ browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
+ }
+
+ expect(element.attr('style')).toContain('border-color: blue');
+ }));
it("should pause the playstate when performing a stagger animation",
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
@@ -1372,6 +1534,86 @@ describe("ngAnimate", function() {
}
}));
+ it("should stagger items and apply the transition + directive styles the right time when piggy-back styles are used",
+ inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement, $window) {
+
+ if(!$sniffer.transitions) return;
+
+ $animate.enabled(true);
+
+ ss.addRule('.stagger-animation.ng-enter, .stagger-animation.ng-leave',
+ '-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
+ 'animation:my_animation 1s 1s, your_animation 1s 2s;');
+
+ ss.addRule('.stagger-animation.ng-enter-stagger, .stagger-animation.ng-leave-stagger',
+ '-webkit-animation-delay:0.1s;' +
+ 'animation-delay:0.1s;');
+
+ var styles = {
+ from : { left : '50px' },
+ to : { left : '100px' }
+ };
+ var container = $compile(html(''))($rootScope);
+
+ var elements = [];
+ for(var i = 0; i < 4; i++) {
+ var newScope = $rootScope.$new();
+ var element = $compile('')(newScope);
+ $animate.enter(element, container, null, styles);
+ elements.push(element);
+ }
+
+ $rootScope.$digest();
+
+ for(i = 0; i < 4; i++) {
+ expect(elements[i]).toHaveClass('ng-enter');
+ assertTransitionDuration(elements[i], '2', true);
+ assertLeftStyle(elements[i], '50');
+ }
+
+ $animate.triggerReflow();
+
+ expect(elements[0]).toHaveClass('ng-enter-active');
+ assertLeftStyle(elements[0], '100');
+ assertTransitionDuration(elements[0], '1');
+
+ for(i = 1; i < 4; i++) {
+ expect(elements[i]).not.toHaveClass('ng-enter-active');
+ assertTransitionDuration(elements[i], '1', true);
+ assertLeftStyle(elements[i], '100', true);
+ }
+
+ $timeout.flush(300);
+
+ for(i = 1; i < 4; i++) {
+ expect(elements[i]).toHaveClass('ng-enter-active');
+ assertTransitionDuration(elements[i], '1');
+ assertLeftStyle(elements[i], '100');
+ }
+
+ $timeout.flush();
+
+ for(i = 0; i < 4; i++) {
+ expect(elements[i]).not.toHaveClass('ng-enter');
+ expect(elements[i]).not.toHaveClass('ng-enter-active');
+ assertTransitionDuration(elements[i], '1', true);
+ assertLeftStyle(elements[i], '100');
+ }
+
+ function assertLeftStyle(element, val, not) {
+ var regex = new RegExp('left: ' + val + 'px');
+ var style = element.attr('style');
+ not ? expect(style).not.toMatch(regex)
+ : expect(style).toMatch(regex);
+ }
+
+ function assertTransitionDuration(element, val, not) {
+ var regex = new RegExp('transition:.*' + val + 's');
+ var style = element.attr('style');
+ not ? expect(style).not.toMatch(regex)
+ : expect(style).toMatch(regex);
+ }
+ }));
});
@@ -1741,6 +1983,82 @@ describe("ngAnimate", function() {
}
}));
+ it("should stagger items, apply directive styles but not apply a transition style when the stagger step kicks in",
+ inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement, $window) {
+
+ if(!$sniffer.transitions) return;
+
+ $animate.enabled(true);
+
+ ss.addRule('.stagger-animation.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('.stagger-animation.ng-enter-stagger, .ani.ng-leave-stagger',
+ '-webkit-transition-delay:0.1s;' +
+ 'transition-delay:0.1s;');
+
+ var styles = {
+ from : { left : '155px' },
+ to : { left : '255px' }
+ };
+ var container = $compile(html(''))($rootScope);
+
+ var elements = [];
+ for(var i = 0; i < 4; i++) {
+ var newScope = $rootScope.$new();
+ var element = $compile('')(newScope);
+ $animate.enter(element, container, null, styles);
+ elements.push(element);
+ }
+
+ $rootScope.$digest();
+
+ for(i = 0; i < 4; i++) {
+ expect(elements[i]).toHaveClass('ng-enter');
+ assertLeftStyle(elements[i], '155');
+ }
+
+ $animate.triggerReflow();
+
+ expect(elements[0]).toHaveClass('ng-enter-active');
+ assertLeftStyle(elements[0], '255');
+ assertNoTransitionDuration(elements[0]);
+
+ for(i = 1; i < 4; i++) {
+ expect(elements[i]).not.toHaveClass('ng-enter-active');
+ assertLeftStyle(elements[i], '255', true);
+ }
+
+ $timeout.flush(300);
+
+ for(i = 1; i < 4; i++) {
+ expect(elements[i]).toHaveClass('ng-enter-active');
+ assertNoTransitionDuration(elements[i]);
+ assertLeftStyle(elements[i], '255');
+ }
+
+ $timeout.flush();
+
+ for(i = 0; i < 4; i++) {
+ expect(elements[i]).not.toHaveClass('ng-enter');
+ expect(elements[i]).not.toHaveClass('ng-enter-active');
+ assertNoTransitionDuration(elements[i]);
+ assertLeftStyle(elements[i], '255');
+ }
+
+ function assertLeftStyle(element, val, not) {
+ var regex = new RegExp('left: ' + val + 'px');
+ var style = element.attr('style');
+ not ? expect(style).not.toMatch(regex)
+ : expect(style).toMatch(regex);
+ }
+
+ function assertNoTransitionDuration(element) {
+ var style = element.attr('style');
+ expect(style).not.toMatch(/transition/);
+ }
+ }));
it("should apply a closing timeout to close all pending transitions",
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
@@ -2042,6 +2360,29 @@ describe("ngAnimate", function() {
expect(elements[i].attr('style')).toBeFalsy();
}
}));
+
+ it("should create a piggy-back-transition which has a duration the same as the max keyframe duration if any directive styles are provided",
+ inject(function($compile, $animate, $rootScope, $sniffer) {
+
+ $animate.enabled(true);
+ ss.addRule('.on', '-webkit-transition: 1s linear all; transition: 1s linear all;');
+
+ element = $compile(html('1
'))($rootScope);
+
+ $animate.addClass(element, 'on', {
+ to: {color: 'red'}
+ });
+
+ $rootScope.$digest();
+ if ($sniffer.transitions) {
+ $animate.triggerReflow();
+ expect(element.attr('style')).toContain('color: red');
+ expect(element.attr('style')).not.toContain('transition');
+ browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
+ }
+
+ expect(element.attr('style')).toContain('color: red');
+ }));
});
@@ -2472,9 +2813,9 @@ describe("ngAnimate", function() {
};
function capture(event) {
- return function(element, add, remove, done) {
+ return function(element, add, remove, styles, done) {
//some animations only have one extra param
- done = done || remove || add;
+ done = arguments[arguments.length-2]; //the last one is the styles array
captures[event]=done;
};
}
@@ -2491,28 +2832,40 @@ describe("ngAnimate", function() {
$compile(element)($rootScope);
assertTempClass('enter', 'temp-enter', function() {
- $animate.enter(element, container, null, 'temp-enter');
+ $animate.enter(element, container, null, {
+ tempClasses: 'temp-enter'
+ });
});
assertTempClass('move', 'temp-move', function() {
- $animate.move(element, null, container2, 'temp-move');
+ $animate.move(element, null, container2, {
+ tempClasses: 'temp-move'
+ });
});
assertTempClass('addClass', 'temp-add', function() {
- $animate.addClass(element, 'add', 'temp-add');
+ $animate.addClass(element, 'add', {
+ tempClasses: 'temp-add'
+ });
});
assertTempClass('removeClass', 'temp-remove', function() {
- $animate.removeClass(element, 'add', 'temp-remove');
+ $animate.removeClass(element, 'add', {
+ tempClasses: 'temp-remove'
+ });
});
element.addClass('remove');
assertTempClass('setClass', 'temp-set', function() {
- $animate.setClass(element, 'add', 'remove', 'temp-set');
+ $animate.setClass(element, 'add', 'remove', {
+ tempClasses: 'temp-set'
+ });
});
assertTempClass('leave', 'temp-leave', function() {
- $animate.leave(element, 'temp-leave');
+ $animate.leave(element, {
+ tempClasses: 'temp-leave'
+ });
});
function assertTempClass(event, className, animationOperation) {