OLD | NEW |
| (Empty) |
1 // Copyright 2014 Google Inc. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 (function(shared, testing) { | |
16 | |
17 var fills = 'backwards|forwards|both'.split('|'); | |
18 var directions = 'reverse|alternate|alternate-reverse'.split('|'); | |
19 | |
20 function makeTiming(timingInput, forGroup) { | |
21 var timing = { | |
22 delay: 0, | |
23 endDelay: 0, | |
24 fill: forGroup ? 'both' : 'none', | |
25 iterationStart: 0, | |
26 iterations: 1, | |
27 duration: forGroup ? 'auto' : 0, | |
28 playbackRate: 1, | |
29 direction: 'normal', | |
30 easing: 'linear', | |
31 }; | |
32 if (typeof timingInput == 'number' && !isNaN(timingInput)) { | |
33 timing.duration = timingInput; | |
34 } else if (timingInput !== undefined) { | |
35 Object.getOwnPropertyNames(timingInput).forEach(function(property) { | |
36 if (timingInput[property] != 'auto') { | |
37 if (typeof timing[property] == 'number' || property == 'duration') { | |
38 if (typeof timingInput[property] != 'number' || isNaN(timingInput[pr
operty])) { | |
39 return; | |
40 } | |
41 } | |
42 if ((property == 'fill') && (fills.indexOf(timingInput[property]) == -
1)) { | |
43 return; | |
44 } | |
45 if ((property == 'direction') && (directions.indexOf(timingInput[prope
rty]) == -1)) { | |
46 return; | |
47 } | |
48 if (property == 'playbackRate' && shared.isDeprecated('AnimationTiming
.playbackRate', '2014-11-28', 'Use AnimationPlayer.playbackRate instead.')) { | |
49 return; | |
50 } | |
51 timing[property] = timingInput[property]; | |
52 } | |
53 }); | |
54 } | |
55 return timing; | |
56 } | |
57 | |
58 function normalizeTimingInput(timingInput, forGroup) { | |
59 var timing = makeTiming(timingInput, forGroup); | |
60 timing.easing = toTimingFunction(timing.easing); | |
61 return timing; | |
62 } | |
63 | |
64 function cubic(a, b, c, d) { | |
65 if (a < 0 || a > 1 || c < 0 || c > 1) { | |
66 return linear; | |
67 } | |
68 return function(x) { | |
69 var start = 0, end = 1; | |
70 while (1) { | |
71 var mid = (start + end) / 2; | |
72 function f(a, b, m) { return 3 * a * (1 - m) * (1 - m) * m + 3 * b * (1
- m) * m * m + m * m * m}; | |
73 var xEst = f(a, c, mid); | |
74 if (Math.abs(x - xEst) < 0.001) { | |
75 return f(b, d, mid); | |
76 } | |
77 if (xEst < x) { | |
78 start = mid; | |
79 } else { | |
80 end = mid; | |
81 } | |
82 } | |
83 } | |
84 } | |
85 | |
86 var Start = 1; | |
87 var Middle = 0.5; | |
88 var End = 0; | |
89 | |
90 function step(count, pos) { | |
91 return function(x) { | |
92 if (x >= 1) { | |
93 return 1; | |
94 } | |
95 var stepSize = 1 / count; | |
96 x += pos * stepSize; | |
97 return x - x % stepSize; | |
98 } | |
99 } | |
100 | |
101 var presets = { | |
102 'ease': cubic(0.25, 0.1, 0.25, 1), | |
103 'ease-in': cubic(0.42, 0, 1, 1), | |
104 'ease-out': cubic(0, 0, 0.58, 1), | |
105 'ease-in-out': cubic(0.42, 0, 0.58, 1), | |
106 'step-start': step(1, Start), | |
107 'step-middle': step(1, Middle), | |
108 'step-end': step(1, End) | |
109 }; | |
110 | |
111 var numberString = '\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*'; | |
112 var cubicBezierRe = new RegExp('cubic-bezier\\(' + numberString + ',' + number
String + ',' + numberString + ',' + numberString + '\\)'); | |
113 var stepRe = /steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/; | |
114 var linear = function(x) { return x; }; | |
115 | |
116 function toTimingFunction(easing) { | |
117 var cubicData = cubicBezierRe.exec(easing); | |
118 if (cubicData) { | |
119 return cubic.apply(this, cubicData.slice(1).map(Number)); | |
120 } | |
121 var stepData = stepRe.exec(easing); | |
122 if (stepData) { | |
123 return step(Number(stepData[1]), {'start': Start, 'middle': Middle, 'end':
End}[stepData[2]]); | |
124 } | |
125 var preset = presets[easing]; | |
126 if (preset) { | |
127 return preset; | |
128 } | |
129 return linear; | |
130 }; | |
131 | |
132 function calculateActiveDuration(timing) { | |
133 return Math.abs(repeatedDuration(timing) / timing.playbackRate); | |
134 } | |
135 | |
136 function repeatedDuration(timing) { | |
137 return timing.duration * timing.iterations; | |
138 } | |
139 | |
140 var PhaseNone = 0; | |
141 var PhaseBefore = 1; | |
142 var PhaseAfter = 2; | |
143 var PhaseActive = 3; | |
144 | |
145 function calculatePhase(activeDuration, localTime, timing) { | |
146 if (localTime == null) { | |
147 return PhaseNone; | |
148 } | |
149 if (localTime < timing.delay) { | |
150 return PhaseBefore; | |
151 } | |
152 if (localTime >= timing.delay + activeDuration) { | |
153 return PhaseAfter; | |
154 } | |
155 return PhaseActive; | |
156 } | |
157 | |
158 function calculateActiveTime(activeDuration, fillMode, localTime, phase, delay
) { | |
159 switch (phase) { | |
160 case PhaseBefore: | |
161 if (fillMode == 'backwards' || fillMode == 'both') | |
162 return 0; | |
163 return null; | |
164 case PhaseActive: | |
165 return localTime - delay; | |
166 case PhaseAfter: | |
167 if (fillMode == 'forwards' || fillMode == 'both') | |
168 return activeDuration; | |
169 return null; | |
170 case PhaseNone: | |
171 return null; | |
172 } | |
173 } | |
174 | |
175 function calculateScaledActiveTime(activeDuration, activeTime, startOffset, ti
ming) { | |
176 return (timing.playbackRate < 0 ? activeTime - activeDuration : activeTime)
* timing.playbackRate + startOffset; | |
177 } | |
178 | |
179 function calculateIterationTime(iterationDuration, repeatedDuration, scaledAct
iveTime, startOffset, timing) { | |
180 if (scaledActiveTime === Infinity || scaledActiveTime === -Infinity || (scal
edActiveTime - startOffset == repeatedDuration && timing.iterations && ((timing.
iterations + timing.iterationStart) % 1 == 0))) { | |
181 return iterationDuration; | |
182 } | |
183 | |
184 return scaledActiveTime % iterationDuration; | |
185 } | |
186 | |
187 function calculateCurrentIteration(iterationDuration, iterationTime, scaledAct
iveTime, timing) { | |
188 if (scaledActiveTime === 0) { | |
189 return 0; | |
190 } | |
191 if (iterationTime == iterationDuration) { | |
192 return timing.iterationStart + timing.iterations - 1; | |
193 } | |
194 return Math.floor(scaledActiveTime / iterationDuration); | |
195 } | |
196 | |
197 function calculateTransformedTime(currentIteration, iterationDuration, iterati
onTime, timing) { | |
198 var currentIterationIsOdd = currentIteration % 2 >= 1; | |
199 var currentDirectionIsForwards = timing.direction == 'normal' || timing.dire
ction == (currentIterationIsOdd ? 'alternate-reverse' : 'alternate'); | |
200 var directedTime = currentDirectionIsForwards ? iterationTime : iterationDur
ation - iterationTime; | |
201 var timeFraction = directedTime / iterationDuration; | |
202 return iterationDuration * timing.easing(timeFraction); | |
203 } | |
204 | |
205 function calculateTimeFraction(activeDuration, localTime, timing) { | |
206 var phase = calculatePhase(activeDuration, localTime, timing); | |
207 var activeTime = calculateActiveTime(activeDuration, timing.fill, localTime,
phase, timing.delay); | |
208 if (activeTime === null) | |
209 return null; | |
210 if (activeDuration === 0) | |
211 return phase === PhaseBefore ? 0 : 1; | |
212 var startOffset = timing.iterationStart * timing.duration; | |
213 var scaledActiveTime = calculateScaledActiveTime(activeDuration, activeTime,
startOffset, timing); | |
214 var iterationTime = calculateIterationTime(timing.duration, repeatedDuration
(timing), scaledActiveTime, startOffset, timing); | |
215 var currentIteration = calculateCurrentIteration(timing.duration, iterationT
ime, scaledActiveTime, timing); | |
216 return calculateTransformedTime(currentIteration, timing.duration, iteration
Time, timing) / timing.duration; | |
217 } | |
218 | |
219 shared.makeTiming = makeTiming; | |
220 shared.normalizeTimingInput = normalizeTimingInput; | |
221 shared.calculateActiveDuration = calculateActiveDuration; | |
222 shared.calculateTimeFraction = calculateTimeFraction; | |
223 shared.calculatePhase = calculatePhase; | |
224 shared.toTimingFunction = toTimingFunction; | |
225 | |
226 if (WEB_ANIMATIONS_TESTING) { | |
227 testing.normalizeTimingInput = normalizeTimingInput; | |
228 testing.toTimingFunction = toTimingFunction; | |
229 testing.calculateActiveDuration = calculateActiveDuration; | |
230 testing.calculatePhase = calculatePhase; | |
231 testing.PhaseNone = PhaseNone; | |
232 testing.PhaseBefore = PhaseBefore; | |
233 testing.PhaseActive = PhaseActive; | |
234 testing.PhaseAfter = PhaseAfter; | |
235 testing.calculateActiveTime = calculateActiveTime; | |
236 testing.calculateScaledActiveTime = calculateScaledActiveTime; | |
237 testing.calculateIterationTime = calculateIterationTime; | |
238 testing.calculateCurrentIteration = calculateCurrentIteration; | |
239 testing.calculateTransformedTime = calculateTransformedTime; | |
240 } | |
241 | |
242 })(webAnimationsShared, webAnimationsTesting); | |
OLD | NEW |