mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
fix($animate): ensure animations work with directives that share a transclusion
Closes #4716 Closes #4871 Closes #5021 Closes #5278
This commit is contained in:
parent
0e50810c53
commit
958d3d56b1
2 changed files with 95 additions and 19 deletions
|
|
@ -258,6 +258,19 @@ angular.module('ngAnimate', ['ng'])
|
|||
var NG_ANIMATE_CLASS_NAME = 'ng-animate';
|
||||
var rootAnimateState = {running: true};
|
||||
|
||||
function extractElementNode(element) {
|
||||
for(var i = 0; i < element.length; i++) {
|
||||
var elm = element[i];
|
||||
if(elm.nodeType == ELEMENT_NODE) {
|
||||
return elm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isMatchingElement(elm1, elm2) {
|
||||
return extractElementNode(elm1) == extractElementNode(elm2);
|
||||
}
|
||||
|
||||
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
|
||||
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
|
||||
|
||||
|
|
@ -556,7 +569,16 @@ 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, doneCallback) {
|
||||
var currentClassName = element.attr('class') || '';
|
||||
var node = extractElementNode(element);
|
||||
//transcluded directives may sometimes fire an animation using only comment nodes
|
||||
//best to catch this early on to prevent any animation operations from occurring
|
||||
if(!node) {
|
||||
fireDOMOperation();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
var currentClassName = node.className;
|
||||
var classes = currentClassName + ' ' + className;
|
||||
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
|
||||
if (!parentElement) {
|
||||
|
|
@ -760,11 +782,7 @@ angular.module('ngAnimate', ['ng'])
|
|||
}
|
||||
|
||||
function cancelChildAnimations(element) {
|
||||
var node = element[0];
|
||||
if(node.nodeType != ELEMENT_NODE) {
|
||||
return;
|
||||
}
|
||||
|
||||
var node = extractElementNode(element);
|
||||
forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
|
||||
element = angular.element(element);
|
||||
var data = element.data(NG_ANIMATE_STATE);
|
||||
|
|
@ -788,7 +806,7 @@ angular.module('ngAnimate', ['ng'])
|
|||
}
|
||||
|
||||
function cleanup(element) {
|
||||
if(element[0] == $rootElement[0]) {
|
||||
if(isMatchingElement(element, $rootElement)) {
|
||||
if(!rootAnimateState.disabled) {
|
||||
rootAnimateState.running = false;
|
||||
rootAnimateState.structural = false;
|
||||
|
|
@ -802,7 +820,7 @@ angular.module('ngAnimate', ['ng'])
|
|||
function animationsDisabled(element, parentElement) {
|
||||
if (rootAnimateState.disabled) return true;
|
||||
|
||||
if(element[0] == $rootElement[0]) {
|
||||
if(isMatchingElement(element, $rootElement)) {
|
||||
return rootAnimateState.disabled || rootAnimateState.running;
|
||||
}
|
||||
|
||||
|
|
@ -812,7 +830,7 @@ angular.module('ngAnimate', ['ng'])
|
|||
//any animations on it
|
||||
if(parentElement.length === 0) break;
|
||||
|
||||
var isRoot = parentElement[0] == $rootElement[0];
|
||||
var isRoot = isMatchingElement(parentElement, $rootElement);
|
||||
var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
|
||||
var result = state && (!!state.disabled || !!state.running);
|
||||
if(isRoot || result) {
|
||||
|
|
@ -960,7 +978,7 @@ angular.module('ngAnimate', ['ng'])
|
|||
parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
|
||||
parentID = parentCounter;
|
||||
}
|
||||
return parentID + '-' + element[0].className;
|
||||
return parentID + '-' + extractElementNode(element).className;
|
||||
}
|
||||
|
||||
function animateSetup(element, className) {
|
||||
|
|
@ -995,7 +1013,6 @@ angular.module('ngAnimate', ['ng'])
|
|||
return false;
|
||||
}
|
||||
|
||||
var node = element[0];
|
||||
//temporarily disable the transition so that the enter styles
|
||||
//don't animate twice (this is here to avoid a bug in Chrome/FF).
|
||||
var activeClassName = '';
|
||||
|
|
@ -1025,35 +1042,37 @@ angular.module('ngAnimate', ['ng'])
|
|||
}
|
||||
|
||||
function blockTransitions(element) {
|
||||
element[0].style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
|
||||
extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
|
||||
}
|
||||
|
||||
function blockKeyframeAnimations(element) {
|
||||
element[0].style[ANIMATION_PROP] = 'none 0s';
|
||||
extractElementNode(element).style[ANIMATION_PROP] = 'none 0s';
|
||||
}
|
||||
|
||||
function unblockTransitions(element) {
|
||||
var node = element[0], prop = TRANSITION_PROP + PROPERTY_KEY;
|
||||
var prop = TRANSITION_PROP + PROPERTY_KEY;
|
||||
var node = extractElementNode(element);
|
||||
if(node.style[prop] && node.style[prop].length > 0) {
|
||||
node.style[prop] = '';
|
||||
}
|
||||
}
|
||||
|
||||
function unblockKeyframeAnimations(element) {
|
||||
var node = element[0], prop = ANIMATION_PROP;
|
||||
var prop = ANIMATION_PROP;
|
||||
var node = extractElementNode(element);
|
||||
if(node.style[prop] && node.style[prop].length > 0) {
|
||||
element[0].style[prop] = '';
|
||||
node.style[prop] = '';
|
||||
}
|
||||
}
|
||||
|
||||
function animateRun(element, className, activeAnimationComplete) {
|
||||
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
if(!element.hasClass(className) || !data) {
|
||||
var node = extractElementNode(element);
|
||||
if(node.className.indexOf(className) == -1 || !data) {
|
||||
activeAnimationComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
var node = element[0];
|
||||
var timings = data.timings;
|
||||
var stagger = data.stagger;
|
||||
var maxDuration = data.maxDuration;
|
||||
|
|
@ -1096,6 +1115,9 @@ angular.module('ngAnimate', ['ng'])
|
|||
}
|
||||
|
||||
if(appliedStyles.length > 0) {
|
||||
//the element being animated may sometimes contain comment nodes in
|
||||
//the jqLite object, so we're safe to use a single variable to house
|
||||
//the styles since there is always only one element being animated
|
||||
var oldStyle = node.getAttribute('style') || '';
|
||||
node.setAttribute('style', oldStyle + ' ' + style);
|
||||
}
|
||||
|
|
@ -1110,6 +1132,7 @@ angular.module('ngAnimate', ['ng'])
|
|||
element.off(css3AnimationEvents, onAnimationProgress);
|
||||
element.removeClass(activeClassName);
|
||||
animateClose(element, className);
|
||||
var node = extractElementNode(element);
|
||||
for (var i in appliedStyles) {
|
||||
node.style.removeProperty(appliedStyles[i]);
|
||||
}
|
||||
|
|
@ -1209,7 +1232,7 @@ angular.module('ngAnimate', ['ng'])
|
|||
}
|
||||
|
||||
var parentElement = element.parent();
|
||||
var clone = angular.element(element[0].cloneNode());
|
||||
var clone = angular.element(extractElementNode(element).cloneNode());
|
||||
|
||||
//make the element super hidden and override any CSS style values
|
||||
clone.attr('style','position:absolute; top:-9999px; left:-9999px');
|
||||
|
|
|
|||
|
|
@ -2873,5 +2873,58 @@ describe("ngAnimate", function() {
|
|||
|
||||
expect($rootElement.children().length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should properly animate elements with compound directives', function() {
|
||||
var capturedAnimation;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.special', function() {
|
||||
return {
|
||||
enter : function(element, done) {
|
||||
capturedAnimation = 'enter';
|
||||
done();
|
||||
},
|
||||
leave : function(element, done) {
|
||||
capturedAnimation = 'leave';
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer) {
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
$templateCache.put('item-template', 'item: #{{ item }} ');
|
||||
var element = $compile('<div>' +
|
||||
' <div ng-repeat="item in items"' +
|
||||
' ng-include="tpl"' +
|
||||
' class="special"></div>' +
|
||||
'</div>')($rootScope);
|
||||
|
||||
ss.addRule('.special', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$rootScope.tpl = 'item-template';
|
||||
$rootScope.items = [1,2,3];
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(capturedAnimation).toBe('enter');
|
||||
expect(element.text()).toContain('item: #1');
|
||||
|
||||
forEach(element.children(), function(kid) {
|
||||
browserTrigger(kid, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
});
|
||||
$timeout.flush();
|
||||
|
||||
$rootScope.items = [];
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(capturedAnimation).toBe('leave');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue