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

Concurrent class-based animations, Promises API for Animations & Fixes to Staggering Animations #8637

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 49 additions & 53 deletions src/ng/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,20 @@ var $AnimateProvider = ['$provide', function($provide) {
return this.$$classNameFilter;
};

this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) {

function async(fn) {
fn && $$asyncCallback(fn);
this.$get = ['$$q', '$$asyncCallback', function($$q, $$asyncCallback) {

var currentDefer;
function asyncPromise() {
// only serve one instance of a promise in order to save CPU cycles
if (!currentDefer) {
currentDefer = $$q.defer();
currentDefer.promise.cancel = noop; //ngAnimate.$animate provides this
$$asyncCallback(function() {
currentDefer.resolve();
currentDefer = null;
});
}
return currentDefer.promise;
}

/**
Expand Down Expand Up @@ -112,39 +122,34 @@ var $AnimateProvider = ['$provide', function($provide) {
* @name $animate#enter
* @kind function
* @description Inserts the element into the DOM either after the `after` element or
* as the first child within the `parent` element. Once complete, the done() callback
* will be fired (if provided).
* as the first child within the `parent` element. 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 inserted into the DOM
* @param {DOMElement} parent the parent element which will append the element as
* a child (if the after element is not present)
* @param {DOMElement} after the sibling element which will append the element
* after itself
* @param {Function=} done callback function that will be called after the element has been
* inserted into the DOM
* @return {Promise} the animation callback promise
*/
enter : function(element, parent, after, done) {
after
? after.after(element)
: parent.prepend(element);
async(done);
return noop;
enter : function(element, parent, after) {
after ? after.after(element)
: parent.prepend(element);
return asyncPromise();
},

/**
*
* @ngdoc method
* @name $animate#leave
* @kind function
* @description Removes the element from the DOM. Once complete, the done() callback will be
* fired (if provided).
* @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 {Function=} done callback function that will be called after the element has been
* removed from the DOM
* @return {Promise} the animation callback promise
*/
leave : function(element, done) {
leave : function(element) {
element.remove();
async(done);
return noop;
return asyncPromise();
},

/**
Expand All @@ -153,46 +158,43 @@ var $AnimateProvider = ['$provide', function($provide) {
* @name $animate#move
* @kind function
* @description Moves the position of the provided element within the DOM to be placed
* either after the `after` element or inside of the `parent` element. Once complete, the
* done() callback will be fired (if provided).
* either after the `after` element or inside of the `parent` element. 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 moved around within the
* DOM
* @param {DOMElement} parent the parent element where the element will be
* inserted into (if the after element is not present)
* @param {DOMElement} after the sibling element where the element will be
* positioned next to
* @param {Function=} done the callback function (if provided) that will be fired after the
* element has been moved to its new position
* @return {Promise} the animation callback promise
*/
move : function(element, parent, after, done) {
move : function(element, parent, after) {
// 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, done);
return this.enter(element, parent, after);
},

/**
*
* @ngdoc method
* @name $animate#addClass
* @kind function
* @description Adds the provided className CSS class value to the provided element. Once
* complete, the done() callback will be fired (if provided).
* @description Adds the provided className CSS class value to the provided element.
* When the function is called a promise is returned that will be resolved at a later time.
* @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 {Function=} done the callback function (if provided) that will be fired after the
* className value has been added to the element
* @return {Promise} the animation callback promise
*/
addClass : function(element, className, done) {
addClass : function(element, className) {
className = !isString(className)
? (isArray(className) ? className.join(' ') : '')
: className;
forEach(element, function (element) {
jqLiteAddClass(element, className);
});
async(done);
return noop;
return asyncPromise();
},

/**
Expand All @@ -201,22 +203,20 @@ var $AnimateProvider = ['$provide', function($provide) {
* @name $animate#removeClass
* @kind function
* @description Removes the provided className CSS class value from the provided element.
* Once complete, the done() callback will be fired (if provided).
* When the function is called a promise is returned that will be resolved at a later time.
* @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 {Function=} done the callback function (if provided) that will be fired after the
* className value has been removed from the element
* @return {Promise} the animation callback promise
*/
removeClass : function(element, className, done) {
className = isString(className) ?
className :
isArray(className) ? className.join(' ') : '';
removeClass : function(element, className) {
className = !isString(className)
? (isArray(className) ? className.join(' ') : '')
: className;
forEach(element, function (element) {
jqLiteRemoveClass(element, className);
});
async(done);
return noop;
return asyncPromise();
},

/**
Expand All @@ -225,21 +225,17 @@ var $AnimateProvider = ['$provide', function($provide) {
* @name $animate#setClass
* @kind function
* @description Adds and/or removes the given CSS classes to and from the element.
* Once complete, the done() callback will be fired (if provided).
* When the function is called a promise is returned that will be resolved at a later time.
* @param {DOMElement} element the element which will have its CSS classes changed
* 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 {Function=} done the callback function (if provided) that will be fired after the
* CSS classes have been set on the element
* @return {Promise} the animation callback promise
*/
setClass : function(element, add, remove, done) {
forEach(element, function (element) {
jqLiteAddClass(element, add);
jqLiteRemoveClass(element, remove);
});
async(done);
return noop;
setClass : function(element, add, remove) {
this.addClass(element, add);
this.removeClass(element, remove);
return asyncPromise();
},

enabled : noop
Expand Down
11 changes: 5 additions & 6 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,14 +720,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
*/
$updateClass : function(newClasses, oldClasses) {
var toAdd = tokenDifference(newClasses, oldClasses);
var toRemove = tokenDifference(oldClasses, newClasses);
if (toAdd && toAdd.length) {
$animate.addClass(this.$$element, toAdd);
}

if(toAdd.length === 0) {
var toRemove = tokenDifference(oldClasses, newClasses);
if (toRemove && toRemove.length) {
$animate.removeClass(this.$$element, toRemove);
} else if(toRemove.length === 0) {
$animate.addClass(this.$$element, toAdd);
} else {
$animate.setClass(this.$$element, toAdd, toRemove);
}
},

Expand Down
12 changes: 5 additions & 7 deletions src/ng/directive/ngClass.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,13 @@ function classDirective(name, selector) {
function updateClasses (oldClasses, newClasses) {
var toAdd = arrayDifference(newClasses, oldClasses);
var toRemove = arrayDifference(oldClasses, newClasses);
toRemove = digestClassCounts(toRemove, -1);
toAdd = digestClassCounts(toAdd, 1);

if (toAdd.length === 0) {
$animate.removeClass(element, toRemove);
} else if (toRemove.length === 0) {
toRemove = digestClassCounts(toRemove, -1);
if(toAdd && toAdd.length) {
$animate.addClass(element, toAdd);
} else {
$animate.setClass(element, toAdd, toRemove);
}
if(toRemove && toRemove.length) {
$animate.removeClass(element, toRemove);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/ng/directive/ngIf.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ var ngIfDirective = ['$animate', function($animate) {
}
if(block) {
previousElements = getBlockElements(block.clone);
$animate.leave(previousElements, function() {
$animate.leave(previousElements).then(function() {
previousElements = null;
});
block = null;
Expand Down
4 changes: 2 additions & 2 deletions src/ng/directive/ngInclude.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate'
currentScope = null;
}
if(currentElement) {
$animate.leave(currentElement, function() {
$animate.leave(currentElement).then(function() {
previousElement = null;
});
previousElement = currentElement;
Expand Down Expand Up @@ -228,7 +228,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate'
// directives to non existing elements.
var clone = $transclude(newScope, function(clone) {
cleanupLastIncludeContent();
$animate.enter(clone, null, $element, afterAnimation);
$animate.enter(clone, null, $element).then(afterAnimation);
});

currentScope = newScope;
Expand Down
2 changes: 1 addition & 1 deletion src/ng/directive/ngSwitch.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ var ngSwitchDirective = ['$animate', function($animate) {
var selected = getBlockElements(selectedElements[i].clone);
selectedScopes[i].$destroy();
previousElements[i] = selected;
$animate.leave(selected, function() {
$animate.leave(selected).then(function() {
previousElements.splice(i, 1);
});
}
Expand Down
Loading