00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import <Foundation/CPObject.j>
00024
00025 @import "CAMediaTimingFunction.j"
00026
00027
00028
00029
00030
00031
00032 CPAnimationEaseInOut = 0;
00033
00034
00035
00036
00037 CPAnimationEaseIn = 1;
00038
00039
00040
00041
00042 CPAnimationEaseOut = 2;
00043
00044
00045
00046
00047 CPAnimationLinear = 3;
00048
00049 ACTUAL_FRAME_RATE = 0;
00050
00080 @implementation CPAnimation : CPObject
00081 {
00082 CPTimeInterval _lastTime;
00083 CPTimeInterval _duration;
00084
00085 CPAnimationCurve _animationCurve;
00086 CAMediaTimingFunction _timingFunction;
00087
00088 float _frameRate;
00089 float _progress;
00090
00091 id _delegate;
00092 CPTimer _timer;
00093 }
00094
00101 - (id)initWithDuration:(float)aDuration animationCurve:(CPAnimationCurve)anAnimationCurve
00102 {
00103 self = [super init];
00104
00105 if (self)
00106 {
00107 _progress = 0.0;
00108 _duration = MAX(0.0, aDuration);
00109 _frameRate = 60.0;
00110
00111 [self setAnimationCurve:anAnimationCurve];
00112 }
00113
00114 return self;
00115 }
00116
00122 - (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve
00123 {
00124 switch (anAnimationCurve)
00125 {
00126 case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut;
00127 break;
00128
00129 case CPAnimationEaseIn: timingFunctionName = kCAMediaTimingFunctionEaseIn;
00130 break;
00131
00132 case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut;
00133 break;
00134
00135 case CPAnimationLinear: timingFunctionName = kCAMediaTimingFunctionLinear;
00136 break;
00137
00138 default: [CPException raise:CPInvalidArgumentException
00139 reason:"Invalid value provided for animation curve"];
00140 break;
00141 }
00142
00143 _animationCurve = anAnimationCurve;
00144 _timingFunction = [CAMediaTimingFunction functionWithName:timingFunctionName];
00145 }
00146
00150 - (CPAnimationCurve)animationCurve
00151 {
00152 return _animationCurve;
00153 }
00154
00160 - (void)setDuration:(CPTimeInterval)aDuration
00161 {
00162 if (aDuration < 0)
00163 [CPException raise:CPInvalidArgumentException reason:"aDuration can't be negative"];
00164
00165 _duration = aDuration;
00166 }
00167
00171 - (CPTimeInterval)duration
00172 {
00173 return _duration;
00174 }
00175
00181 - (void)setFrameRate:(float)frameRate
00182 {
00183 if (frameRate < 0)
00184 [CPException raise:CPInvalidArgumentException reason:"frameRate can't be negative"];
00185
00186 _frameRate = frameRate;
00187 }
00188
00192 - (float)frameRate
00193 {
00194 return _frameRate;
00195 }
00196
00200 - (id)delegate
00201 {
00202 return _delegate;
00203 }
00204
00209 - (void)setDelegate:(id)aDelegate
00210 {
00211 _delegate = aDelegate;
00212 }
00213
00219 - (void)startAnimation
00220 {
00221
00222 if (_timer || _delegate && [_delegate respondsToSelector:@selector(animationShouldStart:)] && ![_delegate animationShouldStart:self])
00223 return;
00224
00225 if (_progress === 1.0)
00226 _progress = 0.0;
00227
00228 ACTUAL_FRAME_RATE = 0;
00229 _lastTime = new Date();
00230
00231 _timer = [CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(animationTimerDidFire:) userInfo:nil repeats:YES];
00232 }
00233
00234
00235
00236
00237 - (void)animationTimerDidFire:(CPTimer)aTimer
00238 {
00239 var currentTime = new Date(),
00240 progress = MIN(1.0, [self currentProgress] + (currentTime - _lastTime) / (_duration * 1000.0));
00241
00242 _lastTime = currentTime;
00243
00244 ++ACTUAL_FRAME_RATE;
00245
00246 [self setCurrentProgress:progress];
00247
00248 if (progress === 1.0)
00249 {
00250 [_timer invalidate];
00251 _timer = nil;
00252
00253 if ([_delegate respondsToSelector:@selector(animationDidEnd:)])
00254 [_delegate animationDidEnd:self];
00255 }
00256 }
00257
00261 - (void)stopAnimation
00262 {
00263 if (!_timer)
00264 return;
00265
00266 [_timer invalidate];
00267 _timer = nil;
00268
00269 if ([_delegate respondsToSelector:@selector(animationDidStop:)])
00270 [_delegate animationDidStop:self];
00271 }
00272
00277 - (BOOL)isAnimating
00278 {
00279 return _timer;
00280 }
00281
00286 - (void)setCurrentProgress:(float)aProgress
00287 {
00288 _progress = aProgress;
00289 }
00290
00294 - (float)currentProgress
00295 {
00296 return _progress;
00297 }
00298
00302 - (float)currentValue
00303 {
00304 var t = [self currentProgress];
00305
00306 if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
00307 return [_delegate animation:self valueForProgress:t];
00308
00309 var c1 = [],
00310 c2 = [];
00311
00312 [_timingFunction getControlPointAtIndex:1 values:c1];
00313 [_timingFunction getControlPointAtIndex:2 values:c2];
00314
00315 return CubicBezierAtTime(t,c1[0],c1[1],c2[0],c2[1],_duration);
00316 }
00317
00318 @end
00319
00320
00321
00322
00323 var CubicBezierAtTime = function CubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration)
00324 {
00325 var ax=0,bx=0,cx=0,ay=0,by=0,cy=0;
00326
00327 function sampleCurveX(t) {return ((ax*t+bx)*t+cx)*t;};
00328 function sampleCurveY(t) {return ((ay*t+by)*t+cy)*t;};
00329 function sampleCurveDerivativeX(t) {return (3.0*ax*t+2.0*bx)*t+cx;};
00330
00331 function solveEpsilon(duration) {return 1.0/(200.0*duration);};
00332 function solve(x,epsilon) {return sampleCurveY(solveCurveX(x,epsilon));};
00333
00334 function solveCurveX(x,epsilon) {var t0,t1,t2,x2,d2,i;
00335 function fabs(n) {if(n>=0) {return n;}else {return 0-n;}};
00336
00337 for(t2=x, i=0; i<8; i++) {x2=sampleCurveX(t2)-x; if(fabs(x2)<epsilon) {return t2;} d2=sampleCurveDerivativeX(t2); if(fabs(d2)<1e-6) {break;} t2=t2-x2/d2;}
00338
00339 t0=0.0; t1=1.0; t2=x; if(t2<t0) {return t0;} if(t2>t1) {return t1;}
00340 while(t0<t1) {x2=sampleCurveX(t2); if(fabs(x2-x)<epsilon) {return t2;} if(x>x2) {t0=t2;}else {t1=t2;} t2=(t1-t0)*.5+t0;}
00341 return t2;
00342 };
00343
00344 cx=3.0*p1x; bx=3.0*(p2x-p1x)-cx; ax=1.0-cx-bx; cy=3.0*p1y; by=3.0*(p2y-p1y)-cy; ay=1.0-cy-by;
00345
00346 return solve(t, solveEpsilon(duration));
00347 };