![]() |
API 0.9.5
|
00001 /* 00002 * CPAnimation.j 00003 * AppKit 00004 * 00005 * Created by Francisco Tolmasky. 00006 * Copyright 2008, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 00025 00026 /* 00027 @global 00028 @group CPAnimationCurve 00029 */ 00030 CPAnimationEaseInOut = 0; 00031 /* 00032 @global 00033 @group CPAnimationCurve 00034 */ 00035 CPAnimationEaseIn = 1; 00036 /* 00037 @global 00038 @group CPAnimationCurve 00039 */ 00040 CPAnimationEaseOut = 2; 00041 /* 00042 @global 00043 @group CPAnimationCurve 00044 */ 00045 CPAnimationLinear = 3; 00046 00047 ACTUAL_FRAME_RATE = 0; 00048 00078 @implementation CPAnimation : CPObject 00079 { 00080 CPTimeInterval _lastTime; 00081 CPTimeInterval _duration; 00082 00083 CPAnimationCurve _animationCurve; 00084 CAMediaTimingFunction _timingFunction; 00085 00086 float _frameRate; 00087 float _progress; 00088 00089 id _delegate; 00090 CPTimer _timer; 00091 } 00092 00099 - (id)initWithDuration:(float)aDuration animationCurve:(CPAnimationCurve)anAnimationCurve 00100 { 00101 self = [super init]; 00102 00103 if (self) 00104 { 00105 _progress = 0.0; 00106 _duration = MAX(0.0, aDuration); 00107 _frameRate = 60.0; 00108 00109 [self setAnimationCurve:anAnimationCurve]; 00110 } 00111 00112 return self; 00113 } 00114 00120 - (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve 00121 { 00122 switch (anAnimationCurve) 00123 { 00124 case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut; 00125 break; 00126 00127 case CPAnimationEaseIn: timingFunctionName = kCAMediaTimingFunctionEaseIn; 00128 break; 00129 00130 case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut; 00131 break; 00132 00133 case CPAnimationLinear: timingFunctionName = kCAMediaTimingFunctionLinear; 00134 break; 00135 00136 default: [CPException raise:CPInvalidArgumentException 00137 reason:"Invalid value provided for animation curve"]; 00138 break; 00139 } 00140 00141 _animationCurve = anAnimationCurve; 00142 _timingFunction = [CAMediaTimingFunction functionWithName:timingFunctionName]; 00143 } 00144 00148 - (CPAnimationCurve)animationCurve 00149 { 00150 return _animationCurve; 00151 } 00152 00158 - (void)setDuration:(CPTimeInterval)aDuration 00159 { 00160 if (aDuration < 0) 00161 [CPException raise:CPInvalidArgumentException reason:"aDuration can't be negative"]; 00162 00163 _duration = aDuration; 00164 } 00165 00169 - (CPTimeInterval)duration 00170 { 00171 return _duration; 00172 } 00173 00179 - (void)setFrameRate:(float)frameRate 00180 { 00181 if (frameRate < 0) 00182 [CPException raise:CPInvalidArgumentException reason:"frameRate can't be negative"]; 00183 00184 _frameRate = frameRate; 00185 } 00186 00190 - (float)frameRate 00191 { 00192 return _frameRate; 00193 } 00194 00198 - (id)delegate 00199 { 00200 return _delegate; 00201 } 00202 00207 - (void)setDelegate:(id)aDelegate 00208 { 00209 _delegate = aDelegate; 00210 } 00211 00217 - (void)startAnimation 00218 { 00219 // If we're already animating, or our delegate stops us, animate. 00220 if (_timer || _delegate && [_delegate respondsToSelector:@selector(animationShouldStart:)] && ![_delegate animationShouldStart:self]) 00221 return; 00222 00223 if (_progress === 1.0) 00224 _progress = 0.0; 00225 00226 ACTUAL_FRAME_RATE = 0; 00227 _lastTime = new Date(); 00228 00229 _timer = [CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(animationTimerDidFire:) userInfo:nil repeats:YES]; 00230 } 00231 00232 /* 00233 @ignore 00234 */ 00235 - (void)animationTimerDidFire:(CPTimer)aTimer 00236 { 00237 var currentTime = new Date(), 00238 progress = MIN(1.0, [self currentProgress] + (currentTime - _lastTime) / (_duration * 1000.0)); 00239 00240 _lastTime = currentTime; 00241 00242 ++ACTUAL_FRAME_RATE; 00243 00244 [self setCurrentProgress:progress]; 00245 00246 if (progress === 1.0) 00247 { 00248 [_timer invalidate]; 00249 _timer = nil; 00250 00251 if ([_delegate respondsToSelector:@selector(animationDidEnd:)]) 00252 [_delegate animationDidEnd:self]; 00253 } 00254 } 00255 00259 - (void)stopAnimation 00260 { 00261 if (!_timer) 00262 return; 00263 00264 [_timer invalidate]; 00265 _timer = nil; 00266 00267 if ([_delegate respondsToSelector:@selector(animationDidStop:)]) 00268 [_delegate animationDidStop:self]; 00269 } 00270 00275 - (BOOL)isAnimating 00276 { 00277 return _timer; 00278 } 00279 00284 - (void)setCurrentProgress:(float)aProgress 00285 { 00286 _progress = aProgress; 00287 } 00288 00292 - (float)currentProgress 00293 { 00294 return _progress; 00295 } 00296 00300 - (float)currentValue 00301 { 00302 var t = [self currentProgress]; 00303 00304 if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)]) 00305 return [_delegate animation:self valueForProgress:t]; 00306 00307 if (_animationCurve == CPAnimationLinear) 00308 return t; 00309 00310 var c1 = [], 00311 c2 = []; 00312 00313 [_timingFunction getControlPointAtIndex:1 values:c1]; 00314 [_timingFunction getControlPointAtIndex:2 values:c2]; 00315 00316 return CubicBezierAtTime(t, c1[0], c1[1], c2[0], c2[1], _duration); 00317 } 00318 00319 @end 00320 00321 // currently used function to determine time 00322 // 1:1 conversion to js from webkit source files 00323 // UnitBezier.h, WebCore_animation_AnimationBase.cpp 00324 var CubicBezierAtTime = function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) 00325 { 00326 var ax = 0, 00327 bx = 0, 00328 cx = 0, 00329 ay = 0, 00330 by = 0, 00331 cy = 0; 00332 // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. 00333 function sampleCurveX(t) 00334 { 00335 return ((ax * t + bx) * t + cx) * t; 00336 } 00337 00338 function sampleCurveY(t) 00339 { 00340 return ((ay * t + by) * t + cy) * t; 00341 } 00342 00343 function sampleCurveDerivativeX(t) 00344 { 00345 return (3.0 * ax * t + 2.0 * bx) * t + cx; 00346 } 00347 00348 // The epsilon value to pass given that the animation is going to run over |duration| seconds. The longer the animation, the more precision is needed in the timing function result to avoid ugly discontinuities. 00349 function solveEpsilon(duration) 00350 { 00351 return 1.0 / (200.0 * duration); 00352 } 00353 00354 function solve(x, epsilon) 00355 { 00356 return sampleCurveY(solveCurveX(x, epsilon)); 00357 } 00358 00359 // Given an x value, find a parametric value it came from. 00360 function solveCurveX(x, epsilon) 00361 { 00362 var t0, 00363 t1, 00364 t2 = x, 00365 x2, 00366 d2, 00367 i = 0; 00368 00369 // First try a few iterations of Newton's method -- normally very fast. 00370 for (; i < 8; i++) 00371 { 00372 x2 = sampleCurveX(t2) - x; 00373 00374 if (ABS(x2) < epsilon) 00375 return t2; 00376 00377 d2 = sampleCurveDerivativeX(t2); 00378 00379 if (ABS(d2) < 1e-6) 00380 break; 00381 00382 t2 = t2 - x2 / d2; 00383 } 00384 00385 // Fall back to the bisection method for reliability. 00386 t0 = 0.0; 00387 t1 = 1.0; 00388 t2 = x; 00389 00390 if (t2 < t0) 00391 return t0; 00392 00393 if (t2 > t1) 00394 return t1; 00395 00396 while (t0 < t1) 00397 { 00398 x2 = sampleCurveX(t2); 00399 00400 if (ABS(x2 - x) < epsilon) 00401 return t2; 00402 00403 if (x > x2) 00404 t0 = t2; 00405 00406 else 00407 t1 = t2; 00408 00409 t2 = (t1 - t0) * 0.5 + t0; 00410 } 00411 00412 return t2; // Failure. 00413 }; 00414 // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). 00415 cx = 3.0 * p1x; 00416 bx = 3.0 * (p2x - p1x) - cx; 00417 ax = 1.0 - cx - bx; 00418 cy = 3.0 * p1y; 00419 by = 3.0 * (p2y - p1y) - cy; 00420 ay = 1.0 - cy - by; 00421 00422 // Convert from input time to parametric value in curve, then from that to output time. 00423 return solve(t, solveEpsilon(duration)); 00424 };