@@ -881,27 +881,73 @@ angular.module('ngAnimate', ['ng'])
881
881
var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount' ;
882
882
var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey' ;
883
883
var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data' ;
884
- var NG_ANIMATE_FALLBACK_CLASS_NAME = 'ng-animate-start' ;
885
- var NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME = 'ng-animate-active' ;
886
884
var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3 ;
885
+ var CLOSING_TIME_BUFFER = 1.5 ;
886
+ var ONE_SECOND = 1000 ;
887
887
888
+ var animationCounter = 0 ;
888
889
var lookupCache = { } ;
889
890
var parentCounter = 0 ;
891
+ var animationReflowQueue = [ ] ;
892
+ var animationElementQueue = [ ] ;
893
+ var animationTimer ;
894
+ var closingAnimationTime = 0 ;
895
+ var timeOut = false ;
896
+ function afterReflow ( element , callback ) {
897
+ $timeout . cancel ( animationTimer ) ;
890
898
891
- var animationReflowQueue = [ ] , animationTimer , timeOut = false ;
892
- function afterReflow ( callback ) {
893
899
animationReflowQueue . push ( callback ) ;
894
- $timeout . cancel ( animationTimer ) ;
900
+
901
+ var node = extractElementNode ( element ) ;
902
+ element = angular . element ( node ) ;
903
+ animationElementQueue . push ( element ) ;
904
+
905
+ var elementData = element . data ( NG_ANIMATE_CSS_DATA_KEY ) ;
906
+ closingAnimationTime = Math . max ( closingAnimationTime ,
907
+ ( elementData . maxDelay + elementData . maxDuration ) * CLOSING_TIME_BUFFER * ONE_SECOND ) ;
908
+
909
+ //by placing a counter we can avoid an accidental
910
+ //race condition which may close an animation when
911
+ //a follow-up animation is midway in its animation
912
+ elementData . animationCount = animationCounter ;
913
+
895
914
animationTimer = $timeout ( function ( ) {
896
915
forEach ( animationReflowQueue , function ( fn ) {
897
916
fn ( ) ;
898
917
} ) ;
918
+
919
+ //copy the list of elements so that successive
920
+ //animations won't conflict if they're added before
921
+ //the closing animation timeout has run
922
+ var elementQueueSnapshot = [ ] ;
923
+ var animationCounterSnapshot = animationCounter ;
924
+ forEach ( animationElementQueue , function ( elm ) {
925
+ elementQueueSnapshot . push ( elm ) ;
926
+ } ) ;
927
+
928
+ $timeout ( function ( ) {
929
+ closeAllAnimations ( elementQueueSnapshot , animationCounterSnapshot ) ;
930
+ elementQueueSnapshot = null ;
931
+ } , closingAnimationTime , false ) ;
932
+
899
933
animationReflowQueue = [ ] ;
934
+ animationElementQueue = [ ] ;
900
935
animationTimer = null ;
901
936
lookupCache = { } ;
937
+ closingAnimationTime = 0 ;
938
+ animationCounter ++ ;
902
939
} , 10 , false ) ;
903
940
}
904
941
942
+ function closeAllAnimations ( elements , count ) {
943
+ forEach ( elements , function ( element ) {
944
+ var elementData = element . data ( NG_ANIMATE_CSS_DATA_KEY ) ;
945
+ if ( elementData && elementData . animationCount == count ) {
946
+ ( elementData . closeAnimationFn || noop ) ( ) ;
947
+ }
948
+ } ) ;
949
+ }
950
+
905
951
function getElementAnimationDetails ( element , cacheKey ) {
906
952
var data = cacheKey ? lookupCache [ cacheKey ] : null ;
907
953
if ( ! data ) {
@@ -1007,6 +1053,7 @@ angular.module('ngAnimate', ['ng'])
1007
1053
timeout is empty (this would cause a flicker bug normally
1008
1054
in the page. There is also no point in performing an animation
1009
1055
that only has a delay and no duration */
1056
+ var maxDelay = Math . max ( timings . transitionDelay , timings . animationDelay ) ;
1010
1057
var maxDuration = Math . max ( timings . transitionDuration , timings . animationDuration ) ;
1011
1058
if ( maxDuration === 0 ) {
1012
1059
element . removeClass ( className ) ;
@@ -1016,13 +1063,9 @@ angular.module('ngAnimate', ['ng'])
1016
1063
//temporarily disable the transition so that the enter styles
1017
1064
//don't animate twice (this is here to avoid a bug in Chrome/FF).
1018
1065
var activeClassName = '' ;
1019
- if ( timings . transitionDuration > 0 ) {
1020
- element . addClass ( NG_ANIMATE_FALLBACK_CLASS_NAME ) ;
1021
- activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' ' ;
1022
- blockTransitions ( element ) ;
1023
- } else {
1066
+ timings . transitionDuration > 0 ?
1067
+ blockTransitions ( element ) :
1024
1068
blockKeyframeAnimations ( element ) ;
1025
- }
1026
1069
1027
1070
forEach ( className . split ( ' ' ) , function ( klass , i ) {
1028
1071
activeClassName += ( i > 0 ? ' ' : '' ) + klass + '-active' ;
@@ -1032,6 +1075,7 @@ angular.module('ngAnimate', ['ng'])
1032
1075
className : className ,
1033
1076
activeClassName : activeClassName ,
1034
1077
maxDuration : maxDuration ,
1078
+ maxDelay : maxDelay ,
1035
1079
classes : className + ' ' + activeClassName ,
1036
1080
timings : timings ,
1037
1081
stagger : stagger ,
@@ -1066,30 +1110,28 @@ angular.module('ngAnimate', ['ng'])
1066
1110
}
1067
1111
1068
1112
function animateRun ( element , className , activeAnimationComplete ) {
1069
- var data = element . data ( NG_ANIMATE_CSS_DATA_KEY ) ;
1113
+ var elementData = element . data ( NG_ANIMATE_CSS_DATA_KEY ) ;
1070
1114
var node = extractElementNode ( element ) ;
1071
- if ( node . className . indexOf ( className ) == - 1 || ! data ) {
1115
+ if ( node . className . indexOf ( className ) == - 1 || ! elementData ) {
1072
1116
activeAnimationComplete ( ) ;
1073
1117
return ;
1074
1118
}
1075
1119
1076
- var timings = data . timings ;
1077
- var stagger = data . stagger ;
1078
- var maxDuration = data . maxDuration ;
1079
- var activeClassName = data . activeClassName ;
1080
- var maxDelayTime = Math . max ( timings . transitionDelay , timings . animationDelay ) * 1000 ;
1120
+ var timings = elementData . timings ;
1121
+ var stagger = elementData . stagger ;
1122
+ var maxDuration = elementData . maxDuration ;
1123
+ var activeClassName = elementData . activeClassName ;
1124
+ var maxDelayTime = Math . max ( timings . transitionDelay , timings . animationDelay ) * ONE_SECOND ;
1081
1125
var startTime = Date . now ( ) ;
1082
1126
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT ;
1083
- var ii = data . ii ;
1127
+ var ii = elementData . ii ;
1084
1128
1085
- var applyFallbackStyle , style = '' , appliedStyles = [ ] ;
1129
+ var style = '' , appliedStyles = [ ] ;
1086
1130
if ( timings . transitionDuration > 0 ) {
1087
1131
var propertyStyle = timings . transitionPropertyStyle ;
1088
1132
if ( propertyStyle . indexOf ( 'all' ) == - 1 ) {
1089
- applyFallbackStyle = true ;
1090
- var fallbackProperty = $sniffer . msie ? '-ms-zoom' : 'border-spacing' ;
1091
- style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ' ;
1092
- style += CSS_PREFIX + 'transition-duration: ' + timings . transitionDurationStyle + ', ' + timings . transitionDuration + 's; ' ;
1133
+ style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';' ;
1134
+ style += CSS_PREFIX + 'transition-duration: ' + timings . transitionDurationStyle + 's;' ;
1093
1135
appliedStyles . push ( CSS_PREFIX + 'transition-property' ) ;
1094
1136
appliedStyles . push ( CSS_PREFIX + 'transition-duration' ) ;
1095
1137
}
@@ -1098,10 +1140,6 @@ angular.module('ngAnimate', ['ng'])
1098
1140
if ( ii > 0 ) {
1099
1141
if ( stagger . transitionDelay > 0 && stagger . transitionDuration === 0 ) {
1100
1142
var delayStyle = timings . transitionDelayStyle ;
1101
- if ( applyFallbackStyle ) {
1102
- delayStyle += ', ' + timings . transitionDelay + 's' ;
1103
- }
1104
-
1105
1143
style += CSS_PREFIX + 'transition-delay: ' +
1106
1144
prepareStaggerDelay ( delayStyle , stagger . transitionDelay , ii ) + '; ' ;
1107
1145
appliedStyles . push ( CSS_PREFIX + 'transition-delay' ) ;
@@ -1124,19 +1162,24 @@ angular.module('ngAnimate', ['ng'])
1124
1162
1125
1163
element . on ( css3AnimationEvents , onAnimationProgress ) ;
1126
1164
element . addClass ( activeClassName ) ;
1165
+ elementData . closeAnimationFn = function ( ) {
1166
+ onEnd ( ) ;
1167
+ activeAnimationComplete ( ) ;
1168
+ } ;
1169
+ return onEnd ;
1127
1170
1128
1171
// This will automatically be called by $animate so
1129
1172
// there is no need to attach this internally to the
1130
1173
// timeout done method.
1131
- return function onEnd ( cancelled ) {
1174
+ function onEnd ( cancelled ) {
1132
1175
element . off ( css3AnimationEvents , onAnimationProgress ) ;
1133
1176
element . removeClass ( activeClassName ) ;
1134
1177
animateClose ( element , className ) ;
1135
1178
var node = extractElementNode ( element ) ;
1136
1179
for ( var i in appliedStyles ) {
1137
1180
node . style . removeProperty ( appliedStyles [ i ] ) ;
1138
1181
}
1139
- } ;
1182
+ }
1140
1183
1141
1184
function onAnimationProgress ( event ) {
1142
1185
event . stopPropagation ( ) ;
@@ -1202,7 +1245,7 @@ angular.module('ngAnimate', ['ng'])
1202
1245
//data from the element which will not make the 2nd animation
1203
1246
//happen in the first place
1204
1247
var cancel = preReflowCancellation ;
1205
- afterReflow ( function ( ) {
1248
+ afterReflow ( element , function ( ) {
1206
1249
unblockTransitions ( element ) ;
1207
1250
unblockKeyframeAnimations ( element ) ;
1208
1251
//once the reflow is complete then we point cancel to
@@ -1218,7 +1261,6 @@ angular.module('ngAnimate', ['ng'])
1218
1261
1219
1262
function animateClose ( element , className ) {
1220
1263
element . removeClass ( className ) ;
1221
- element . removeClass ( NG_ANIMATE_FALLBACK_CLASS_NAME ) ;
1222
1264
element . removeData ( NG_ANIMATE_CSS_DATA_KEY ) ;
1223
1265
}
1224
1266
@@ -1268,7 +1310,7 @@ angular.module('ngAnimate', ['ng'])
1268
1310
beforeAddClass : function ( element , className , animationCompleted ) {
1269
1311
var cancellationMethod = animateBefore ( element , suffixClasses ( className , '-add' ) ) ;
1270
1312
if ( cancellationMethod ) {
1271
- afterReflow ( function ( ) {
1313
+ afterReflow ( element , function ( ) {
1272
1314
unblockTransitions ( element ) ;
1273
1315
unblockKeyframeAnimations ( element ) ;
1274
1316
animationCompleted ( ) ;
@@ -1285,7 +1327,7 @@ angular.module('ngAnimate', ['ng'])
1285
1327
beforeRemoveClass : function ( element , className , animationCompleted ) {
1286
1328
var cancellationMethod = animateBefore ( element , suffixClasses ( className , '-remove' ) ) ;
1287
1329
if ( cancellationMethod ) {
1288
- afterReflow ( function ( ) {
1330
+ afterReflow ( element , function ( ) {
1289
1331
unblockTransitions ( element ) ;
1290
1332
unblockKeyframeAnimations ( element ) ;
1291
1333
animationCompleted ( ) ;
2 commit comments
marcfallows commentedon Jul 9, 2014
@matsko Not sure if this checkin is the culprit, but your "Rolling out animations with your own directives" example no longer works in 1.2.6+ (but works if I switch the version to 1.2.5). This is the only $animate commit so I'm thinking this could be the cause.
http://plnkr.co/edit/gaoKAVZWeCF5elBdVYjI?p=preview
It works on the first step, but not the subsequent steps (only between step 0 and step 1)
matsko commentedon Jul 14, 2014
@marcfallows looks like 1.2 is failing and 1.3. is fine. I will take a look at it.