refactor(ngAnimate): simplify functions and remove redundant args/calls
Simplifies/Optimizes the following functions: - `areAnimationsAllowed()` - `cleanupEventListeners()` - `closeChildAnimations()` - `clearElementAnimationState()` - `markElementAnimationState()` - `findCallbacks()` Although not its primary aim, this commit also offers a small performance boost to animations (~5% as measured with the `animation-bp` benchmark).
This commit is contained in:
@@ -36,9 +36,9 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
}
|
||||
}
|
||||
|
||||
function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
|
||||
function isAllowed(ruleType, currentAnimation, previousAnimation) {
|
||||
return rules[ruleType].some(function(fn) {
|
||||
return fn(element, currentAnimation, previousAnimation);
|
||||
return fn(currentAnimation, previousAnimation);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,40 +48,40 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
return and ? a && b : a || b;
|
||||
}
|
||||
|
||||
rules.join.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.join.push(function(newAnimation, currentAnimation) {
|
||||
// if the new animation is class-based then we can just tack that on
|
||||
return !newAnimation.structural && hasAnimationClasses(newAnimation);
|
||||
});
|
||||
|
||||
rules.skip.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.skip.push(function(newAnimation, currentAnimation) {
|
||||
// there is no need to animate anything if no classes are being added and
|
||||
// there is no structural animation that will be triggered
|
||||
return !newAnimation.structural && !hasAnimationClasses(newAnimation);
|
||||
});
|
||||
|
||||
rules.skip.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.skip.push(function(newAnimation, currentAnimation) {
|
||||
// why should we trigger a new structural animation if the element will
|
||||
// be removed from the DOM anyway?
|
||||
return currentAnimation.event === 'leave' && newAnimation.structural;
|
||||
});
|
||||
|
||||
rules.skip.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.skip.push(function(newAnimation, currentAnimation) {
|
||||
// if there is an ongoing current animation then don't even bother running the class-based animation
|
||||
return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;
|
||||
});
|
||||
|
||||
rules.cancel.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.cancel.push(function(newAnimation, currentAnimation) {
|
||||
// there can never be two structural animations running at the same time
|
||||
return currentAnimation.structural && newAnimation.structural;
|
||||
});
|
||||
|
||||
rules.cancel.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.cancel.push(function(newAnimation, currentAnimation) {
|
||||
// if the previous animation is already running, but the new animation will
|
||||
// be triggered, but the new animation is structural
|
||||
return currentAnimation.state === RUNNING_STATE && newAnimation.structural;
|
||||
});
|
||||
|
||||
rules.cancel.push(function(element, newAnimation, currentAnimation) {
|
||||
rules.cancel.push(function(newAnimation, currentAnimation) {
|
||||
// cancel the animation if classes added / removed in both animation cancel each other out,
|
||||
// but only if the current animation isn't structural
|
||||
|
||||
@@ -181,10 +181,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
return this === arg || !!(this.compareDocumentPosition(arg) & 16);
|
||||
};
|
||||
|
||||
function findCallbacks(parent, element, event) {
|
||||
var targetNode = getDomNode(element);
|
||||
var targetParentNode = getDomNode(parent);
|
||||
|
||||
function findCallbacks(targetParentNode, targetNode, event) {
|
||||
var matches = [];
|
||||
var entries = callbackRegistry[event];
|
||||
if (entries) {
|
||||
@@ -209,11 +206,11 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
});
|
||||
}
|
||||
|
||||
function cleanupEventListeners(phase, element) {
|
||||
if (phase === 'close' && !element[0].parentNode) {
|
||||
function cleanupEventListeners(phase, node) {
|
||||
if (phase === 'close' && !node.parentNode) {
|
||||
// If the element is not attached to a parentNode, it has been removed by
|
||||
// the domOperation, and we can safely remove the event callbacks
|
||||
$animate.off(element);
|
||||
$animate.off(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,12 +308,9 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// the input data when running `$animateCss`.
|
||||
var options = copy(initialOptions);
|
||||
|
||||
var node, parent;
|
||||
element = stripCommentsFromElement(element);
|
||||
if (element) {
|
||||
node = getDomNode(element);
|
||||
parent = element.parent();
|
||||
}
|
||||
var node = getDomNode(element);
|
||||
var parentNode = node && node.parentNode;
|
||||
|
||||
options = prepareAnimationOptions(options);
|
||||
|
||||
@@ -381,7 +375,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// there is no point in traversing the same collection of parent ancestors if a followup
|
||||
// animation will be run on the same element that already did all that checking work
|
||||
if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state !== PRE_DIGEST_STATE)) {
|
||||
skipAnimations = !areAnimationsAllowed(element, parent, event);
|
||||
skipAnimations = !areAnimationsAllowed(node, parentNode, event);
|
||||
}
|
||||
|
||||
if (skipAnimations) {
|
||||
@@ -393,7 +387,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
}
|
||||
|
||||
if (isStructural) {
|
||||
closeChildAnimations(element);
|
||||
closeChildAnimations(node);
|
||||
}
|
||||
|
||||
var newAnimation = {
|
||||
@@ -408,7 +402,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
};
|
||||
|
||||
if (hasExistingAnimation) {
|
||||
var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation);
|
||||
var skipAnimationFlag = isAllowed('skip', newAnimation, existingAnimation);
|
||||
if (skipAnimationFlag) {
|
||||
if (existingAnimation.state === RUNNING_STATE) {
|
||||
close();
|
||||
@@ -418,7 +412,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
return existingAnimation.runner;
|
||||
}
|
||||
}
|
||||
var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
|
||||
var cancelAnimationFlag = isAllowed('cancel', newAnimation, existingAnimation);
|
||||
if (cancelAnimationFlag) {
|
||||
if (existingAnimation.state === RUNNING_STATE) {
|
||||
// this will end the animation right away and it is safe
|
||||
@@ -440,7 +434,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// a joined animation means that this animation will take over the existing one
|
||||
// so an example would involve a leave animation taking over an enter. Then when
|
||||
// the postDigest kicks in the enter will be ignored.
|
||||
var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
|
||||
var joinAnimationFlag = isAllowed('join', newAnimation, existingAnimation);
|
||||
if (joinAnimationFlag) {
|
||||
if (existingAnimation.state === RUNNING_STATE) {
|
||||
normalizeAnimationDetails(element, newAnimation);
|
||||
@@ -474,7 +468,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
|
||||
if (!isValidAnimation) {
|
||||
close();
|
||||
clearElementAnimationState(element);
|
||||
clearElementAnimationState(node);
|
||||
return runner;
|
||||
}
|
||||
|
||||
@@ -482,7 +476,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
var counter = (existingAnimation.counter || 0) + 1;
|
||||
newAnimation.counter = counter;
|
||||
|
||||
markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation);
|
||||
markElementAnimationState(node, PRE_DIGEST_STATE, newAnimation);
|
||||
|
||||
$rootScope.$$postDigest(function() {
|
||||
var animationDetails = activeAnimationsLookup.get(node);
|
||||
@@ -523,7 +517,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// isn't allowed to animate from here then we need to clear the state of the element
|
||||
// so that any future animations won't read the expired animation data.
|
||||
if (!isValidAnimation) {
|
||||
clearElementAnimationState(element);
|
||||
clearElementAnimationState(node);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -535,7 +529,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
? 'setClass'
|
||||
: animationDetails.event;
|
||||
|
||||
markElementAnimationState(element, RUNNING_STATE);
|
||||
markElementAnimationState(node, RUNNING_STATE);
|
||||
var realRunner = $$animation(element, event, animationDetails.options);
|
||||
|
||||
// this will update the runner's flow-control events based on
|
||||
@@ -547,7 +541,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
close(!status);
|
||||
var animationDetails = activeAnimationsLookup.get(node);
|
||||
if (animationDetails && animationDetails.counter === counter) {
|
||||
clearElementAnimationState(getDomNode(element));
|
||||
clearElementAnimationState(node);
|
||||
}
|
||||
notifyProgress(runner, event, 'close', {});
|
||||
});
|
||||
@@ -557,7 +551,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
|
||||
function notifyProgress(runner, event, phase, data) {
|
||||
runInNextPostDigestOrNow(function() {
|
||||
var callbacks = findCallbacks(parent, element, event);
|
||||
var callbacks = findCallbacks(parentNode, node, event);
|
||||
if (callbacks.length) {
|
||||
// do not optimize this call here to RAF because
|
||||
// we don't know how heavy the callback code here will
|
||||
@@ -567,10 +561,10 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
forEach(callbacks, function(callback) {
|
||||
callback(element, phase, data);
|
||||
});
|
||||
cleanupEventListeners(phase, element);
|
||||
cleanupEventListeners(phase, node);
|
||||
});
|
||||
} else {
|
||||
cleanupEventListeners(phase, element);
|
||||
cleanupEventListeners(phase, node);
|
||||
}
|
||||
});
|
||||
runner.progress(event, phase, data);
|
||||
@@ -585,8 +579,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
}
|
||||
}
|
||||
|
||||
function closeChildAnimations(element) {
|
||||
var node = getDomNode(element);
|
||||
function closeChildAnimations(node) {
|
||||
var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
|
||||
forEach(children, function(child) {
|
||||
var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME), 10);
|
||||
@@ -604,16 +597,11 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
});
|
||||
}
|
||||
|
||||
function clearElementAnimationState(element) {
|
||||
var node = getDomNode(element);
|
||||
function clearElementAnimationState(node) {
|
||||
node.removeAttribute(NG_ANIMATE_ATTR_NAME);
|
||||
activeAnimationsLookup.remove(node);
|
||||
}
|
||||
|
||||
function isMatchingElement(nodeOrElmA, nodeOrElmB) {
|
||||
return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
|
||||
}
|
||||
|
||||
/**
|
||||
* This fn returns false if any of the following is true:
|
||||
* a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed
|
||||
@@ -621,54 +609,54 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
* c) the element is not a child of the body
|
||||
* d) the element is not a child of the $rootElement
|
||||
*/
|
||||
function areAnimationsAllowed(element, parentElement, event) {
|
||||
var bodyElement = jqLite($document[0].body);
|
||||
var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
|
||||
var rootElementDetected = isMatchingElement(element, $rootElement);
|
||||
var parentAnimationDetected = false;
|
||||
var animateChildren;
|
||||
var elementDisabled = disabledElementsLookup.get(getDomNode(element));
|
||||
function areAnimationsAllowed(node, parentNode, event) {
|
||||
var bodyNode = $document[0].body;
|
||||
var rootNode = getDomNode($rootElement);
|
||||
|
||||
var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);
|
||||
var bodyNodeDetected = (node === bodyNode) || node.nodeName === 'HTML';
|
||||
var rootNodeDetected = (node === rootNode);
|
||||
var parentAnimationDetected = false;
|
||||
var elementDisabled = disabledElementsLookup.get(node);
|
||||
var animateChildren;
|
||||
|
||||
var parentHost = jqLite.data(node, NG_ANIMATE_PIN_DATA);
|
||||
if (parentHost) {
|
||||
parentElement = parentHost;
|
||||
parentNode = getDomNode(parentHost);
|
||||
}
|
||||
|
||||
parentElement = getDomNode(parentElement);
|
||||
|
||||
while (parentElement) {
|
||||
if (!rootElementDetected) {
|
||||
while (parentNode) {
|
||||
if (!rootNodeDetected) {
|
||||
// angular doesn't want to attempt to animate elements outside of the application
|
||||
// therefore we need to ensure that the rootElement is an ancestor of the current element
|
||||
rootElementDetected = isMatchingElement(parentElement, $rootElement);
|
||||
rootNodeDetected = (parentNode === rootNode);
|
||||
}
|
||||
|
||||
if (parentElement.nodeType !== ELEMENT_NODE) {
|
||||
if (parentNode.nodeType !== ELEMENT_NODE) {
|
||||
// no point in inspecting the #document element
|
||||
break;
|
||||
}
|
||||
|
||||
var details = activeAnimationsLookup.get(parentElement) || {};
|
||||
var details = activeAnimationsLookup.get(parentNode) || {};
|
||||
// either an enter, leave or move animation will commence
|
||||
// therefore we can't allow any animations to take place
|
||||
// but if a parent animation is class-based then that's ok
|
||||
if (!parentAnimationDetected) {
|
||||
var parentElementDisabled = disabledElementsLookup.get(parentElement);
|
||||
var parentNodeDisabled = disabledElementsLookup.get(parentNode);
|
||||
|
||||
if (parentElementDisabled === true && elementDisabled !== false) {
|
||||
if (parentNodeDisabled === true && elementDisabled !== false) {
|
||||
// disable animations if the user hasn't explicitly enabled animations on the
|
||||
// current element
|
||||
elementDisabled = true;
|
||||
// element is disabled via parent element, no need to check anything else
|
||||
break;
|
||||
} else if (parentElementDisabled === false) {
|
||||
} else if (parentNodeDisabled === false) {
|
||||
elementDisabled = false;
|
||||
}
|
||||
parentAnimationDetected = details.structural;
|
||||
}
|
||||
|
||||
if (isUndefined(animateChildren) || animateChildren === true) {
|
||||
var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);
|
||||
var value = jqLite.data(parentNode, NG_ANIMATE_CHILDREN_DATA);
|
||||
if (isDefined(value)) {
|
||||
animateChildren = value;
|
||||
}
|
||||
@@ -677,40 +665,39 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
|
||||
// there is no need to continue traversing at this point
|
||||
if (parentAnimationDetected && animateChildren === false) break;
|
||||
|
||||
if (!bodyElementDetected) {
|
||||
if (!bodyNodeDetected) {
|
||||
// we also need to ensure that the element is or will be a part of the body element
|
||||
// otherwise it is pointless to even issue an animation to be rendered
|
||||
bodyElementDetected = isMatchingElement(parentElement, bodyElement);
|
||||
bodyNodeDetected = (parentNode === bodyNode);
|
||||
}
|
||||
|
||||
if (bodyElementDetected && rootElementDetected) {
|
||||
if (bodyNodeDetected && rootNodeDetected) {
|
||||
// If both body and root have been found, any other checks are pointless,
|
||||
// as no animation data should live outside the application
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rootElementDetected) {
|
||||
// If no rootElement is detected, check if the parentElement is pinned to another element
|
||||
parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);
|
||||
if (!rootNodeDetected) {
|
||||
// If `rootNode` is not detected, check if `parentNode` is pinned to another element
|
||||
parentHost = jqLite.data(parentNode, NG_ANIMATE_PIN_DATA);
|
||||
if (parentHost) {
|
||||
// The pin target element becomes the next parent element
|
||||
parentElement = getDomNode(parentHost);
|
||||
parentNode = getDomNode(parentHost);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
parentElement = parentElement.parentNode;
|
||||
parentNode = parentNode.parentNode;
|
||||
}
|
||||
|
||||
var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
|
||||
return allowAnimation && rootElementDetected && bodyElementDetected;
|
||||
return allowAnimation && rootNodeDetected && bodyNodeDetected;
|
||||
}
|
||||
|
||||
function markElementAnimationState(element, state, details) {
|
||||
function markElementAnimationState(node, state, details) {
|
||||
details = details || {};
|
||||
details.state = state;
|
||||
|
||||
var node = getDomNode(element);
|
||||
node.setAttribute(NG_ANIMATE_ATTR_NAME, state);
|
||||
|
||||
var oldValue = activeAnimationsLookup.get(node);
|
||||
|
||||
Reference in New Issue
Block a user