生活和艺术同样,最美的永远是曲线。 -- 爱德华布尔沃 - 利顿git
在第九章“图层时间”中,咱们讨论了动画时间和CAMediaTiming
协议。如今咱们来看一下另外一个和时间相关的机制--所谓的缓冲。Core Animation使用缓冲来使动画移动更平滑更天然,而不是看起来的那种机械和人工,在这一章咱们将要研究如何对你的动画控制和自定义缓冲曲线。github
动画实际上就是一段时间内的变化,这就暗示了变化必定是随着某个特定的速率进行。速率由如下公式计算而来:数组
velocity = change / time;
这里的变化能够指的是一个物体移动的距离,时间指动画持续的时长,用这样的一个移动能够更加形象的描述(好比position
和bounds
属性的动画),但实际上它应用于任意能够作动画的属性(好比color
和opacity
)。app
上面的等式假设了速度在整个动画过程当中都是恒定不变的(就如同第八章“显式动画”的状况),对于这种恒定速度的动画咱们称之为“线性步调”,并且从技术的角度而言这也是实现动画最简单的方式,但也是彻底不真实的一种效果。iview
考虑一个场景,一辆车行驶在必定距离内,它并不会一开始就以60mph的速度行驶,而后到达终点后忽然变成0mph。一是由于须要无限大的加速度(即便是最好的车也不会在0秒内从0跑到60),另外否则的话会干死全部乘客。在现实中,它会慢慢地加速到全速,而后当它接近终点的时候,它会慢慢地减速,直到最后停下来。函数
那么对于一个掉落到地上的物体又会怎样呢?它会首先停在空中,而后一直加速到落到地面,而后忽然中止(而后因为积累的动能转换伴随着一声巨响,砰!)。测试
现实生活中的任何一个物体都会在运动中加速或者减速。那么咱们如何在动画中实现这种加速度呢?一种方法是使用物理引擎来对运动物体的摩擦和动量来建模,然而这会使得计算过于复杂。咱们称这种类型的方程为缓冲函数,幸运的是,Core Animation内嵌了一系列标准函数提供给咱们使用。动画
CAMediaTimingFunction
那么该如何使用缓冲方程式呢?首先须要设置CAAnimation
的timingFunction
属性,是CAMediaTimingFunction
类的一个对象。若是想改变隐式动画的计时函数,一样也可使用CATransaction
的+setAnimationTimingFunction:
方法。ui
这里有一些方式来建立CAMediaTimingFunction
,最简单的方式是调用 +timingFunctionWithName: 的构造方法。这里传入以下几个常量之一:atom
kCAMediaTimingFunctionLinear kCAMediaTimingFunctionEaseIn kCAMediaTimingFunctionEaseOut kCAMediaTimingFunctionEaseInEaseOut kCAMediaTimingFunctionDefault
kCAMediaTimingFunctionLinear
选项建立了一个线性的计时函数,一样也是CAAnimation
的timingFunction
属性为空时候的默认函数。线性步调对于那些当即加速而且保持匀速到达终点的场景会有意义(例如射出枪膛的子弹),可是默认来讲它看起来很奇怪,由于对大多数的动画来讲确实不多用到。
kCAMediaTimingFunctionEaseIn
常量建立了一个慢慢加速而后忽然中止的方法。对于以前提到的自由落体的例子来讲很适合,或者好比对准一个目标的导弹的发射。
kCAMediaTimingFunctionEaseOut
则偏偏相反,它以一个全速开始,而后慢慢减速中止。它有一个削弱的效果,应用的场景好比一扇门慢慢地关上,而不是砰地一声。
kCAMediaTimingFunctionEaseInEaseOut
建立了一个慢慢加速而后再慢慢减速的过程。这是现实世界大多数物体移动的方式,也是大多数动画来讲最好的选择。若是只能够用一种缓冲函数的话,那就必须是它了。那么你会疑惑为何这不是默认的选择,实际上当使用UIView
的动画方法时,他的确是默认的,但当建立CAAnimation
的时候,就须要手动设置它了。
最后还有一个kCAMediaTimingFunctionDefault
,它和kCAMediaTimingFunctionEaseInEaseOut
很相似,可是加速和减速的过程都稍微有些慢。它和kCAMediaTimingFunctionEaseInEaseOut
的区别很难察觉,多是苹果以为它对于隐式动画来讲更适合(而后对UIKit就改变了想法,而是使用kCAMediaTimingFunctionEaseInEaseOut
做为默认效果),虽然它的名字说是默认的,但仍是要记住当建立显式的CAAnimation
它并非默认选项(换句话说,默认的图层行为动画用kCAMediaTimingFunctionDefault
做为它们的计时方法)。
你可使用一个简单的测试工程来实验一下(清单10.1),在运行以前改变缓冲函数的代码,而后点击任何地方来观察图层是如何经过指定的缓冲移动的。
清单10.1 缓冲函数的简单测试
@interface ViewController () @property (nonatomic, strong) CALayer *colorLayer;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //create a red layer self.colorLayer = [CALayer layer]; self.colorLayer.frame = CGRectMake(0, 0, 100, 100); self.colorLayer.position = CGPointMake(self.view.bounds.size.width/2.0, self.view.bounds.size.height/2.0); self.colorLayer.backgroundColor = [UIColor redColor].CGColor; [self.view.layer addSublayer:self.colorLayer]; }- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ //configure the transaction [CATransaction begin]; [CATransaction setAnimationDuration:1.0]; [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; //set the position self.colorLayer.position = [[touches anyObject] locationInView:self.view]; //commit transaction [CATransaction commit]; }@end
UIView
的动画缓冲UIKit的动画也一样支持这些缓冲方法的使用,尽管语法和常量有些不一样,为了改变UIView
动画的缓冲选项,给options
参数添加以下常量之一:
UIViewAnimationOptionCurveEaseInOut UIViewAnimationOptionCurveEaseIn UIViewAnimationOptionCurveEaseOut UIViewAnimationOptionCurveLinear
它们和CAMediaTimingFunction
紧密关联,UIViewAnimationOptionCurveEaseInOut
是默认值(这里没有kCAMediaTimingFunctionDefault
相对应的值了)。
具体使用方法见清单10.2(注意到这里再也不使用UIView
额外添加的图层,由于UIKit的动画并不支持这类图层)。
清单10.2 使用UIKit动画的缓冲测试工程
@interface ViewController () @property (nonatomic, strong) UIView *colorView;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //create a red layer self.colorView = [[UIView alloc] init]; self.colorView.bounds = CGRectMake(0, 0, 100, 100); self.colorView.center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2); self.colorView.backgroundColor = [UIColor redColor]; [self.view addSubview:self.colorView]; }- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ //perform the animation [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ //set the position self.colorView.center = [[touches anyObject] locationInView:self.view]; } completion:NULL]; }@end
或许你会回想起第八章里面颜色切换的关键帧动画因为线性变换的缘由(见清单8.5)看起来有些奇怪,使得颜色变换很是不天然。为了纠正这点,咱们来用更加合适的缓冲方法,例如kCAMediaTimingFunctionEaseIn
,给图层的颜色变化添加一点脉冲效果,让它更像现实中的一个彩色灯泡。
咱们不想给整个动画过程应用这个效果,咱们但愿对每一个动画的过程重复这样的缓冲,因而每次颜色的变换都会有脉冲效果。
CAKeyframeAnimation
有一个NSArray
类型的timingFunctions
属性,咱们能够用它来对每次动画的步骤指定不一样的计时函数。可是指定函数的个数必定要等于keyframes
数组的元素个数减一,由于它是描述每一帧之间动画速度的函数。
在这个例子中,咱们自始至终想使用同一个缓冲函数,但咱们一样须要一个函数的数组来告诉动画不停地重复每一个步骤,而不是在整个动画序列只作一次缓冲,咱们简单地使用包含多个相同函数拷贝的数组就能够了(见清单10.3)。
运行更新后的代码,你会发现动画看起来更加天然了。
清单10.3 对CAKeyframeAnimation
使用CAMediaTimingFunction
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *layerView; @property (nonatomic, weak) IBOutlet CALayer *colorLayer;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //create sublayer self.colorLayer = [CALayer layer]; self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f); self.colorLayer.backgroundColor = [UIColor blueColor].CGColor; //add it to our view [self.layerView.layer addSublayer:self.colorLayer]; }- (IBAction)changeColor { //create a keyframe animation CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"backgroundColor"; animation.duration = 2.0; animation.values = @[ (__bridge id)[UIColor blueColor].CGColor, (__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor greenColor].CGColor, (__bridge id)[UIColor blueColor].CGColor ]; //add timing function CAMediaTimingFunction *fn = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn]; animation.timingFunctions = @[fn, fn, fn]; //apply animation to layer [self.colorLayer addAnimation:animation forKey:nil]; }@end