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 timing[property] = timingInput[property]; |
| 49 } |
| 50 }); |
| 51 } |
| 52 return timing; |
| 53 } |
| 54 |
| 55 function normalizeTimingInput(timingInput, forGroup) { |
| 56 var timing = makeTiming(timingInput, forGroup); |
| 57 timing.easing = toTimingFunction(timing.easing); |
| 58 return timing; |
| 59 } |
| 60 |
| 61 function cubic(a, b, c, d) { |
| 62 if (a < 0 || a > 1 || c < 0 || c > 1) { |
| 63 return linear; |
| 64 } |
| 65 return function(x) { |
| 66 var start = 0, end = 1; |
| 67 while (1) { |
| 68 var mid = (start + end) / 2; |
| 69 function f(a, b, m) { return 3 * a * (1 - m) * (1 - m) * m + 3 * b * (1
- m) * m * m + m * m * m}; |
| 70 var xEst = f(a, c, mid); |
| 71 if (Math.abs(x - xEst) < 0.001) { |
| 72 return f(b, d, mid); |
| 73 } |
| 74 if (xEst < x) { |
| 75 start = mid; |
| 76 } else { |
| 77 end = mid; |
| 78 } |
| 79 } |
| 80 } |
| 81 } |
| 82 |
| 83 var Start = 1; |
| 84 var Middle = 0.5; |
| 85 var End = 0; |
| 86 |
| 87 function step(count, pos) { |
| 88 return function(x) { |
| 89 if (x >= 1) { |
| 90 return 1; |
| 91 } |
| 92 var stepSize = 1 / count; |
| 93 x += pos * stepSize; |
| 94 return x - x % stepSize; |
| 95 } |
| 96 } |
| 97 |
| 98 var presets = { |
| 99 'ease': cubic(0.25, 0.1, 0.25, 1), |
| 100 'ease-in': cubic(0.42, 0, 1, 1), |
| 101 'ease-out': cubic(0, 0, 0.58, 1), |
| 102 'ease-in-out': cubic(0.42, 0, 0.58, 1), |
| 103 'step-start': step(1, Start), |
| 104 'step-middle': step(1, Middle), |
| 105 'step-end': step(1, End) |
| 106 }; |
| 107 |
| 108 var numberString = '\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*'; |
| 109 var cubicBezierRe = new RegExp('cubic-bezier\\(' + numberString + ',' + number
String + ',' + numberString + ',' + numberString + '\\)'); |
| 110 var stepRe = /steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/; |
| 111 var linear = function(x) { return x; }; |
| 112 |
| 113 function toTimingFunction(easing) { |
| 114 var cubicData = cubicBezierRe.exec(easing); |
| 115 if (cubicData) { |
| 116 return cubic.apply(this, cubicData.slice(1).map(Number)); |
| 117 } |
| 118 var stepData = stepRe.exec(easing); |
| 119 if (stepData) { |
| 120 return step(Number(stepData[1]), {'start': Start, 'middle': Middle, 'end':
End}[stepData[2]]); |
| 121 } |
| 122 var preset = presets[easing]; |
| 123 if (preset) { |
| 124 return preset; |
| 125 } |
| 126 return linear; |
| 127 }; |
| 128 |
| 129 function calculateActiveDuration(timing) { |
| 130 return Math.abs(repeatedDuration(timing) / timing.playbackRate); |
| 131 } |
| 132 |
| 133 function repeatedDuration(timing) { |
| 134 return timing.duration * timing.iterations; |
| 135 } |
| 136 |
| 137 var PhaseNone = 0; |
| 138 var PhaseBefore = 1; |
| 139 var PhaseAfter = 2; |
| 140 var PhaseActive = 3; |
| 141 |
| 142 function calculatePhase(activeDuration, localTime, timing) { |
| 143 if (localTime == null) { |
| 144 return PhaseNone; |
| 145 } |
| 146 if (localTime < timing.delay) { |
| 147 return PhaseBefore; |
| 148 } |
| 149 if (localTime >= timing.delay + activeDuration) { |
| 150 return PhaseAfter; |
| 151 } |
| 152 return PhaseActive; |
| 153 } |
| 154 |
| 155 function calculateActiveTime(activeDuration, fillMode, localTime, phase, delay
) { |
| 156 switch (phase) { |
| 157 case PhaseBefore: |
| 158 if (fillMode == 'backwards' || fillMode == 'both') |
| 159 return 0; |
| 160 return null; |
| 161 case PhaseActive: |
| 162 return localTime - delay; |
| 163 case PhaseAfter: |
| 164 if (fillMode == 'forwards' || fillMode == 'both') |
| 165 return activeDuration; |
| 166 return null; |
| 167 case PhaseNone: |
| 168 return null; |
| 169 } |
| 170 } |
| 171 |
| 172 function calculateScaledActiveTime(activeDuration, activeTime, startOffset, ti
ming) { |
| 173 return (timing.playbackRate < 0 ? activeTime - activeDuration : activeTime)
* timing.playbackRate + startOffset; |
| 174 } |
| 175 |
| 176 function calculateIterationTime(iterationDuration, repeatedDuration, scaledAct
iveTime, startOffset, timing) { |
| 177 if (scaledActiveTime === Infinity || scaledActiveTime === -Infinity || (scal
edActiveTime - startOffset == repeatedDuration && timing.iterations && ((timing.
iterations + timing.iterationStart) % 1 == 0))) { |
| 178 return iterationDuration; |
| 179 } |
| 180 |
| 181 return scaledActiveTime % iterationDuration; |
| 182 } |
| 183 |
| 184 function calculateCurrentIteration(iterationDuration, iterationTime, scaledAct
iveTime, timing) { |
| 185 if (scaledActiveTime === 0) { |
| 186 return 0; |
| 187 } |
| 188 if (iterationTime == iterationDuration) { |
| 189 return timing.iterationStart + timing.iterations - 1; |
| 190 } |
| 191 return Math.floor(scaledActiveTime / iterationDuration); |
| 192 } |
| 193 |
| 194 function calculateTransformedTime(currentIteration, iterationDuration, iterati
onTime, timing) { |
| 195 var currentIterationIsOdd = currentIteration % 2 >= 1; |
| 196 var currentDirectionIsForwards = timing.direction == 'normal' || timing.dire
ction == (currentIterationIsOdd ? 'alternate-reverse' : 'alternate'); |
| 197 var directedTime = currentDirectionIsForwards ? iterationTime : iterationDur
ation - iterationTime; |
| 198 var timeFraction = directedTime / iterationDuration; |
| 199 return iterationDuration * timing.easing(timeFraction); |
| 200 } |
| 201 |
| 202 function calculateTimeFraction(activeDuration, localTime, timing) { |
| 203 var phase = calculatePhase(activeDuration, localTime, timing); |
| 204 var activeTime = calculateActiveTime(activeDuration, timing.fill, localTime,
phase, timing.delay); |
| 205 if (activeTime === null) |
| 206 return null; |
| 207 if (activeDuration === 0) |
| 208 return phase === PhaseBefore ? 0 : 1; |
| 209 var startOffset = timing.iterationStart * timing.duration; |
| 210 var scaledActiveTime = calculateScaledActiveTime(activeDuration, activeTime,
startOffset, timing); |
| 211 var iterationTime = calculateIterationTime(timing.duration, repeatedDuration
(timing), scaledActiveTime, startOffset, timing); |
| 212 var currentIteration = calculateCurrentIteration(timing.duration, iterationT
ime, scaledActiveTime, timing); |
| 213 return calculateTransformedTime(currentIteration, timing.duration, iteration
Time, timing) / timing.duration; |
| 214 } |
| 215 |
| 216 shared.makeTiming = makeTiming; |
| 217 shared.normalizeTimingInput = normalizeTimingInput; |
| 218 shared.calculateActiveDuration = calculateActiveDuration; |
| 219 shared.calculateTimeFraction = calculateTimeFraction; |
| 220 shared.calculatePhase = calculatePhase; |
| 221 shared.toTimingFunction = toTimingFunction; |
| 222 |
| 223 if (WEB_ANIMATIONS_TESTING) { |
| 224 testing.normalizeTimingInput = normalizeTimingInput; |
| 225 testing.toTimingFunction = toTimingFunction; |
| 226 testing.calculateActiveDuration = calculateActiveDuration; |
| 227 testing.calculatePhase = calculatePhase; |
| 228 testing.PhaseNone = PhaseNone; |
| 229 testing.PhaseBefore = PhaseBefore; |
| 230 testing.PhaseActive = PhaseActive; |
| 231 testing.PhaseAfter = PhaseAfter; |
| 232 testing.calculateActiveTime = calculateActiveTime; |
| 233 testing.calculateScaledActiveTime = calculateScaledActiveTime; |
| 234 testing.calculateIterationTime = calculateIterationTime; |
| 235 testing.calculateCurrentIteration = calculateCurrentIteration; |
| 236 testing.calculateTransformedTime = calculateTransformedTime; |
| 237 } |
| 238 |
| 239 })(webAnimationsShared, webAnimationsTesting); |
OLD | NEW |