-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathanimateDecorator.js
175 lines (144 loc) · 6.62 KB
/
animateDecorator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
'use strict';
var forEach = angular.forEach;
angular.module('ngTouchNav')
.config(function($animateProvider) {
$animateProvider.register('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) {
var noop = angular.noop;
var forEach = angular.forEach;
//one day all browsers will have these properties
var w3cAnimationProp = 'animation';
var w3cTransitionProp = 'transition';
//but some still use vendor-prefixed styles
var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
var durationKey = 'Duration',
delayKey = 'Delay',
animationIterationCountKey = 'IterationCount',
ELEMENT_NODE = 1;
function animate(element, className, done) {
if (!($sniffer.transitions || $sniffer.animations)) {
done();
return;
}
else if(['ng-enter','ng-leave','ng-move'].indexOf(className) == -1) {
var existingDuration = 0;
forEach(element, function(element) {
if (element.nodeType == ELEMENT_NODE) {
var elementStyles = $window.getComputedStyle(element) || {};
existingDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
parseMaxTime(elementStyles[vendorTransitionProp + durationKey]),
existingDuration);
}
});
if(existingDuration > 0) {
done();
return;
}
}
element.addClass(className);
//we want all the styles defined before and after
var duration = 0;
forEach(element, function(element) {
if (element.nodeType == ELEMENT_NODE) {
var elementStyles = $window.getComputedStyle(element) || {};
var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
if(animationDuration > 0) {
animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
1);
}
duration = Math.max(animationDelay + animationDuration,
transitionDelay + transitionDuration,
duration);
}
});
/* there is no point in performing a reflow if the animation
timeout is empty (this would cause a flicker bug normally
in the page */
if(duration > 0) {
var activeClassName = '';
forEach(className.split(' '), function(klass, i) {
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
});
$timeout(function() {
element.addClass(activeClassName);
//if the end events don't fire, run the callback 50ms later anyway
$timeout(done, duration * 1000 + 50, false);
/* listen for animation events instead of parsing
computed styles for a correctly synced animation */
//one day all browsers will have these events
var w3cAnimationEvent = 'animationend';
var w3cTransitionEvent = 'transitionend';
//but some still use vendor-prefixed events
var vendorAnimationEvent = $sniffer.vendorPrefix.toLowerCase() + 'AnimationEnd';
var vendorTransitionEvent = $sniffer.vendorPrefix.toLowerCase() + 'TransitionEnd';
var events = [w3cAnimationEvent, vendorAnimationEvent, w3cTransitionEvent, vendorTransitionEvent].join(' ');
var callback = function() {
console.warn('callback');
element.off(events, callback);
$timeout(done, 0, false);
};
element.on(events, callback);
},0,false);
//this will automatically be called by $animate so
//there is no need to attach this internally to the
//timeout done method
return function onEnd(cancelled) {
element.removeClass(className);
element.removeClass(activeClassName);
//only when the animation is cancelled is the done()
//function not called for this animation therefore
//this must be also called
if(cancelled) {
done();
}
};
}
else {
element.removeClass(className);
done();
}
function parseMaxTime(str) {
var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
forEach(values, function(value) {
total = Math.max(parseFloat(value) || 0, total);
});
return total;
}
}
return {
enter : function(element, done) {
return animate(element, 'ng-enter', done);
},
leave : function(element, done) {
return animate(element, 'ng-leave', done);
},
move : function(element, done) {
return animate(element, 'ng-move', done);
},
addClass : function(element, className, done) {
return animate(element, suffixClasses(className, '-add'), done);
},
removeClass : function(element, className, done) {
return animate(element, suffixClasses(className, '-remove'), done);
}
};
}]);
function suffixClasses(classes, suffix) {
var className = '';
classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
forEach(classes, function(klass, i) {
if(klass && klass.length > 0) {
className += (i > 0 ? ' ' : '') + klass + suffix;
}
});
return className;
}
});