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

Increase performance when checking if element is animatable #13784

Merged
merged 2 commits into from
Jan 19, 2016
Merged
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
44 changes: 28 additions & 16 deletions src/ngAnimate/animateQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,13 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
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
* b) a parent element has an ongoing structural animation, and animateChildren is false
* 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';
Expand Down Expand Up @@ -604,10 +611,12 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
if (!parentAnimationDetected) {
var parentElementDisabled = disabledElementsLookup.get(parentNode);

// disable animations if the user hasn't explicitly enabled animations on the
// current element
if (parentElementDisabled === 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) {
elementDisabled = false;
}
Expand All @@ -624,25 +633,28 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// there is no need to continue traversing at this point
if (parentAnimationDetected && animateChildren === false) break;

if (!rootElementDetected) {
// 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);
if (!rootElementDetected) {
parentHost = parentElement.data(NG_ANIMATE_PIN_DATA);
if (parentHost) {
parentElement = parentHost;
rootElementDetected = true;
}
}
}

if (!bodyElementDetected) {
// we also need to ensure that the element is or will be apart of the body element
// 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);
}

if (bodyElementDetected && rootElementDetected) {
// 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 = parentElement.data(NG_ANIMATE_PIN_DATA);
if (parentHost) {
// The pin target element becomes the next parent element
parentElement = parentHost;
continue;
}
}

parentElement = parentElement.parent();
}

Expand Down
106 changes: 84 additions & 22 deletions test/ngAnimate/animateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1483,10 +1483,14 @@ describe("animations", function() {
return new $$AnimateRunner();
};
});

return function($animate) {
$animate.enabled(true);
};
}));

it('should throw if the arguments are not elements',
inject(function($animate, $compile, $document, $rootScope, $rootElement) {
inject(function($animate, $rootElement) {

var element = jqLite('<div></div>');

Expand All @@ -1505,7 +1509,7 @@ describe("animations", function() {
they('should animate an element inside a pinned element that is the $prop element',
['same', 'parent', 'grandparent'],
function(elementRelation) {
inject(function($animate, $compile, $document, $rootElement, $rootScope) {
inject(function($animate, $document, $rootElement, $rootScope) {

var pinElement, animateElement;

Expand Down Expand Up @@ -1543,34 +1547,92 @@ describe("animations", function() {
});
});

it('should adhere to the disabled state of the hosted parent when an element is pinned',
inject(function($animate, $compile, $document, $rootElement, $rootScope) {
they('should not animate an element when the pinned ($prop) element, is pinned to an element that is not a child of the $rootElement',
['same', 'parent', 'grandparent'],
function(elementRelation) {
inject(function($animate, $document, $rootElement, $rootScope) {

var pinElement, animateElement, pinTargetElement = jqLite('<div></div>');

var innerParent = jqLite('<div></div>');
jqLite($document[0].body).append(innerParent);
innerParent.append($rootElement);
var innerChild = jqLite('<div></div>');
$rootElement.append(innerChild);
var innerParent = jqLite('<div></div>');
jqLite($document[0].body).append(innerParent);
innerParent.append($rootElement);

var element = jqLite('<div></div>');
jqLite($document[0].body).append(element);
switch (elementRelation) {
case 'same':
pinElement = jqLite('<div id="animate"></div>');
break;
case 'parent':
pinElement = jqLite('<div><div id="animate"></div></div>');
break;
case 'grandparent':
pinElement = jqLite('<div><div><div id="animate"></div></div></div>');
break;
}

$animate.pin(element, innerChild);
// Append both the pin element and the pinTargetElement outside the app root
jqLite($document[0].body).append(pinElement);
jqLite($document[0].body).append(pinTargetElement);

$animate.enabled(innerChild, false);
animateElement = jqLite($document[0].getElementById('animate'));

$animate.addClass(element, 'blue');
$rootScope.$digest();
expect(capturedAnimation).toBeFalsy();
$animate.addClass(animateElement, 'red');
$rootScope.$digest();
expect(capturedAnimation).toBeFalsy();

$animate.enabled(innerChild, true);
$animate.pin(pinElement, pinTargetElement);

$animate.addClass(element, 'red');
$rootScope.$digest();
expect(capturedAnimation).toBeTruthy();
$animate.addClass(animateElement, 'blue');
$rootScope.$digest();
expect(capturedAnimation).toBeFalsy();

dealoc(element);
}));
dealoc(pinElement);
});
});

they('should adhere to the disabled state of the hosted parent when the $prop element is pinned',
['same', 'parent', 'grandparent'],
function(elementRelation) {
inject(function($animate, $document, $rootElement, $rootScope) {

var pinElement, animateElement, pinHostElement = jqLite('<div></div>');

var innerParent = jqLite('<div></div>');
jqLite($document[0].body).append(innerParent);
innerParent.append($rootElement);

switch (elementRelation) {
case 'same':
pinElement = jqLite('<div id="animate"></div>');
break;
case 'parent':
pinElement = jqLite('<div><div id="animate"></div></div>');
break;
case 'grandparent':
pinElement = jqLite('<div><div><div id="animate"></div></div></div>');
break;
}

$rootElement.append(pinHostElement);
jqLite($document[0].body).append(pinElement);
animateElement = jqLite($document[0].getElementById('animate'));

$animate.pin(pinElement, pinHostElement);
$animate.enabled(pinHostElement, false);

$animate.addClass(animateElement, 'blue');
$rootScope.$digest();
expect(capturedAnimation).toBeFalsy();

$animate.enabled(pinHostElement, true);

$animate.addClass(animateElement, 'red');
$rootScope.$digest();
expect(capturedAnimation).toBeTruthy();

dealoc(pinElement);
});
});
});

describe('callbacks', function() {
Expand Down