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 |