CAAnimation
是一个动画抽象类,但是不要直接使用CAAnimation
类,而是使用它的子类,如上图所示展示的就是它的家族成员子类们,CAAnimation
遵守CAMediaTiming
和CAAction
协议,它是直接作用在CALayer
上的,并非UIView
上,动画执行过程不在主线程上进行,所以不会阻塞主线程。先来看看CAnimation
都定义了哪些属性,方法。
+ animation
初始化一个CAAnimation 对象
(instancetype)animation;---
是一个动画抽象类,但是不要直接使用类,而是使用它的子类,如上图所示展示的就是它的家族成员子类们,遵守`CAMediaTiming`和`CAAction`协议,它是直接作用在`CALayer`上的,并非`UIView`上,动画执行过程不在主线程上进行,所以不会阻塞主线程。先来看看`CAnimation`都定义了哪些属性,方法。
初始化一个CAAnimation 对象
- (instancetype)animation;
```
+defaultValueForKey:
- shouldArchiveValueForKey:
后者通过传入一个关键字对动画对象 进行序列化本地存储,并且返回是否成功。然后使用相同的关键字调用前者来获取这个持久化的对象。
+ (nullable id)defaultValueForKey:(NSString *)key;
- (BOOL)shouldArchiveValueForKey:(NSString *)key;
Timing Function (CAMediaTimingFunction)
Timing Function会被用于变化起点到终点之间的插值计算,形象的说是Timing Function决定了动画运行的节奏(Pacing),比如均匀变化(相同时间变化量相同),先快后慢,先慢后快还是不停变化速度。
Timing Function对应的类是CAMediaTimingFunction
。
@property(nullable, strong) CAMediaTimingFunction *timingFunction;
CAMediaTimingFunction
提供了两种获得时间函数的方法,一种是使用系统提供的五种时间函数,一种是自定义的时间函数。先来看第一种:
+ (id)functionWithName:(NSString *)name;
设置不同的name
就能调用不同的系统提供的时间函数,name
的可选值有:
/** Timing function names. **/
//线性 :匀速(速度不变,加速度为0)
A_EXTERN NSString * const kCAMediaTimingFunctionLinear
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
//渐进:动画缓慢进入,然后加速到达目的地
CA_EXTERN NSString * const kCAMediaTimingFunctionEaseIn
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
//渐出:动画全速进入,然后再减速到达目的地
CA_EXTERN NSString * const kCAMediaTimingFunctionEaseOut
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
//渐进渐出:动画缓慢进入,中间加速,然后减速到达目的地,这个是默认的动画行为
CA_EXTERN NSString * const kCAMediaTimingFunctionEaseInEaseOut
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
CA_EXTERN NSString * const kCAMediaTimingFunctionDefault
__OSX_AVAILABLE_STARTING (__MAC_10_6, __IPHONE_3_0);
我们来看下系统提供的这五种时间函数的具体效果:(Ease的意思是缓和)
- Ease in (
kCAMediaTimingFunctionEaseIn
):
![](http://upload-images.jianshu.io/upload_images/276769-8e66fc4e8369fc4c.gif?imageMogr2/auto-orient/strip)
- Ease out (
kCAMediaTimingFunctionEaseOut
):
![](http://upload-images.jianshu.io/upload_images/276769-97bc05022596c53a.gif?imageMogr2/auto-orient/strip)
- Ease in ease out (
kCAMediaTimingFunctionEaseInEaseOut
):
![](http://upload-images.jianshu.io/upload_images/276769-25bc023663bf848e.gif?imageMogr2/auto-orient/strip)
- 默认 (kCAMediaTimingFunctionDefault
):
![](http://upload-images.jianshu.io/upload_images/276769-8de7f89e5091e4cd.gif?imageMogr2/auto-orient/strip)
cubic-bezier() 时间函数
你也可以使用+ functionWithControlPoints::::
或者- initWithControlPoints::::
自定义自己的时间函数(cubic-bezier() )。
cubic-bezier() 定义了一条立方贝塞尔曲线(cubic Bézier curves)。这些曲线是连续的,一般用于动画的平滑变换,也被成为缓动函数(easing functions)。
系统提供的四种时间函数的曲线图(不包含默认的kCAMediaTimingFunctionDefault):
![](http://upload-images.jianshu.io/upload_images/276769-51a4a967a8e54563.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
纵轴x(t)的取值范围是0.0-1.0,横轴t的取值也是0.0-1.0,真实动画的持续时间,值的变化范围都是不定的,所以要按照比例进行换算,这个比较好理解就不细讲了。
纵轴x(t)代表的是值改变的量,横轴t代表时间。
值的改变量的意思就是当前时间的值与初始值的差值(是差值并不是当前时刻的值,因为原点是(0,0)),这个值的类型有很多下面会细讲。
形象一点的话你可以把x(t)看作当前位置距离起点的距离,那么速度 v = dx/dt , 加速度 a = dv/dt(求导)。所以通过这个曲线就可以表现出:动画在不同时刻的差值(与初始值相比较),值变化的速度,以及加速度。当v<0的时候就表示着x(t)的值是负增长,就好像汽车一直行驶的时候,开始倒车了。
这个图理解了,再来看看 Bézier curve (贝塞尔曲线)
贝塞尔曲线于1962年,由法国工程师Pierre Bézier所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau算法开发,以稳定数值的方法求出贝塞尔曲线。
+ functionWithControlPoints::::
或者- initWithControlPoints::::
构建的贝塞尔曲线是一条三次曲线,一条三次贝塞尔曲线如下图所示需要四个点来定义,P0、P1、P2 和 P3。P0和P3是起点和终点,这两个点被作为比例固定在坐标系上,横轴为时间比例,纵轴为完成状态。P0(0, 0)表示初始时间和初始状态,P3是(1,1),表示终止时间和终止状态。上述两个方法的四个参数就是P1 , P2的坐标。
![](http://upload-images.jianshu.io/upload_images/276769-8afe20d291d93a77.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
三次贝塞尔曲线演示动画,t在[0,1]区间
![](http://upload-images.jianshu.io/upload_images/276769-75f690bd96dcf78e.gif?imageMogr2/auto-orient/strip)
Bézier_3
对贝塞尔曲线不理解的可以多查查相关资料,讲了这么多,如果你想自己自定义一条贝塞尔曲线并看看实际效果可以利用http://cubic-bezier.com/ 这个网站自己动手实验。
![](http://upload-images.jianshu.io/upload_images/276769-4a77b31566eaa5f3.gif?imageMogr2/auto-orient/strip)
演示
+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
- (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
removedOnCompletion
下面会利用CAAnimation
的派生类CABasicAnimation
进行讲解 。
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;
CAAnimationDelegate
//当动画开始的时候被调用
- (void)animationDidStart:(CAAnimation *)anim;
//当动画结束的时候被调用
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
CAPropertyAnimation
CAPropertyAnimation
是CAAnimation
的子类,CAAnimation
定义的属性,声明的方法以及遵循的协议,CAPropertyAnimation
也都全部适用。CAPropertyAnimation
有两个子类CABasicAnimation
和CAKeyframeAnimation
,先来看下CAPropertyAnimation
。
创建一个新的animation 对象,并且设置它的属性keyPath
。
+ (instancetype)animationWithKeyPath:(nullable NSString *)path;
@property(nullable, copy) NSString *keyPath;
keyPath
文档是这么描述的 The key-path describing the property to be animated.这个keyPath其实对应的是layer
的属性,设置了KeyPath就代表着,这个keyPath对应的layer
属性将会做动画。
关键来了,layer
到底是有多少个属性是可以用来做动画的?你上网查是查不完全的,但是你可以查询CALayer
的API文档:
/** Geometry and layer hierarchy properties. **/
/* The bounds of the layer. Defaults to CGRectZero. Animatable. */
@property CGRect bounds;
描述中只要包含Animatable
的属性就都可以用来做动画。
继续进行之前,先看一个layer易混淆的地方:layer
是通过树形结构组织起来的,共有三种树形结构
- model layer tree (模型层树)
- presentation tree (表示层树)
- render tree (渲染层树)
当改变layer的值的时候,model layer tree的值会马上改变,通过render tree渲染,presentation tree以动画的形式展现layer的某个属性值的渐变过程。model layer tree
中的layer
是我们通常意义说的layer
,当我们修改layer的属性的时候,就会立刻修改model layer tree。
view1.layer.anchorPoint = CGPointMake(0.6, 0.6);
refer tree(渲染层树)是动画的真实执行者,但是它是私有的,开发者不能调用。
@property(getter=isAdditive) BOOL additive;
默认是NO,当设置为YES的时候,使 Core Animation 在更新 presentation layer 之前将动画的值添加到 model layer 中去。
详细了解可以参考这篇文章:http://ronnqvi.st/multiple-animations/ 。
@property(getter=isCumulative) BOOL cumulative;
cumulative
一般和repeatCount
(动画重复的次数)结合使用,当cumulative
被设为YES时,下次动画的值会在上一次动画值的基础上进行而不是重新回到初始状态,可以看下面的两个例子,cumulative
分别被设置为YES或者NO。
![](http://upload-images.jianshu.io/upload_images/276769-c7f9f9f814cafa91.gif?imageMogr2/auto-orient/strip)
cumulative = YES
![](http://upload-images.jianshu.io/upload_images/276769-2dba8092054ff1b0.gif?imageMogr2/auto-orient/strip)
cumulative = NO
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
animation.fromValue = @25;
animation.toValue = @100;
//动画重复的次数
animation.repeatCount = 3;
//动画的持续时间
animation.duration = 1;
animation.cumulative = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
[_icon.layer addAnimation:animation forKey:@"animation"];
CABasicAnimation
CABasicAnimation
是CAPropertyAnimation
的子类一般也是使用animationWithKeyPath:
对CABasicAnimation
进行实例化,并指定layer(带有Animatable表示)的属性作为关键路径进行注册。
CAMediaTiming
CAMediaTiming
是CAAnimation
,CALayer
所遵循的协议,上面没提是因为感觉结合具体动画的创建会更加直观。CAMediaTiming
协议定义了一段动画内用来控制逝去时间的属性的结合,可以精准的控制时间。
@property CFTimeInterval beginTime;
指定动画的开始时间,如果动画需要延迟几秒开始应当这样设置:animation.beginTime = CACurrentMediaTime() +?
。
@property CFTimeInterval duration;
动画执行一次
的时长,无限重复可设置为HUGE_VALF
。
@property float speed;
speed
动画执行一次的时间系数,默认是1.0,如果设置了speed
的数值,那么动画执行一次的时间就等于duration /speed
,设置为0动画将不会执行。
@property CFTimeInterval timeOffset;
timeOffset
和speed
不一样,timeOffset
不是一个时间系数,timeOffset
是对时间进行偏移,计算出动画在那个时刻的状态然后开始执行。
timeOffset = Duration > timeOffset ? timeOffset : (Duration % timeOffset)
有一点需要注意,不论timeOffset的值为多少,动画从哪个状态开始执行,动画都会完整执行一次(比如timeOffset = 3.0 , Duration = 6.0;那么动画将会从时间等于3秒的状态开始执行,然后回到初始状态执行 0 ~ 3的动画)。
![](http://upload-images.jianshu.io/upload_images/276769-1520d91e16a584d9.gif?imageMogr2/auto-orient/strip)
timeOffset = 3.0 , Duration = 6.0
//你可以自己测试一下,设置一下repeatCount看看效果,也可以设置一下speed的值
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
animation.duration = 6;
animation.fromValue = @25;
animation.toValue = @225;
animation.timeOffset = 3;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeBackwards;
[_icon.layer addAnimation:self.animation2 forKey:@"animation2"];
@property float repeatCount;
动画执行(重复)的次数,默认是0。
@property CFTimeInterval repeatDuration;
可以理解为动画执行的总时长,duration
是动画执行一次的时长,两者不一样,默认值为0,不要和repeatCount
共同使用。
@property BOOL autoreverses;
用来设置动画是否有回放的效果,默认为NO。
@property(copy) NSString *fillMode;
fillModel:填充模式。有四个可选值,只有在removeOnCompletion
设置为NO的时候才会生效。removeOnCompletion
:动画结束时是否自动移除,如果设置为YES,那么你要手动给layer赋值,否则layer会自动回到动画开始之前的状态。
layer动画运行的过程:其实我们给一个视图添加 layer动画时,真正移动并不是我们视图本身,而是presentation layer 的一个缓存,动画开始的时候presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除原始layer显示,这就解释了为什么我们的视图在结束后又回到了原的状态,因为它根本没动过。
KCAFillModelRemoved 这个是默认值,也就是说动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态 , 动画将在设置的beginTime开始执行(如果没有设置beginTime,则动画立即执行)。
![](http://upload-images.jianshu.io/upload_images/276769-73a6454a3db7f103.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
KCAFillModelRemoved
KACFillModelForwards 当动画结束后,layer会一直保持着动画最后的状态。
![](http://upload-images.jianshu.io/upload_images/276769-2de1257bf05da36c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
KACFillModelForwards
KCAFillModeBackwards 会立即执行动画的第一帧,不论是否设置了beginTime属性。
![](http://upload-images.jianshu.io/upload_images/276769-5cc188ba945d85d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
KCAFillModeBackwards
KCAFillModeBoth 动画加入开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态。
![](http://upload-images.jianshu.io/upload_images/276769-599f559edd7f06c2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
KCAFillModeBoth
以上是CAMediaTiming
的内容,继续看CABasicAnimation
三个关于layer值的属性。
@property(nullable, strong) id fromValue;
@property(nullable, strong) id toValue;
@property(nullable, strong) id byValue;
fromValue
: KeyPath对应layer属性 的初始值。toValue
: keyPath对应layer属性 的结束值。
伴随动画的进行,在duration
的持续时间内,keyPath对应的layer属性值从fromValue渐渐地变为toValue。byValue
表示动画期间改变的值,以layer的position.x
举例,当fromeValue = @100;
byValue = @100;
就表示着layer的position.x
初始值为100 ,动画期间position.x
改变的值是100,position.x
从初始值100变为200。toValue
和byValue
不要同时使用,作用冲突。
CABasicAnimation
实践
CAKeyframeAnimation
关键帧(keyframe)使我们能够定义动画中任意的一个点,然后让Core Animation填充所谓的中间帧。CAKeyframeAnimation
与CABasicAnimation
虽然都是CAPropertyAnimation
的子类但是两者还是有区别的:CABasicAnimation
:只能让keyPath
对应的layer
的某个可动画(Animatable)的属性,从初始值(fromValue)转变到另外一个数值(toValue)。CAKeyframeAnimation
:可以使用一个数组NSArray *values
,保存这些数值(数值的个数不定),并使用数组NSArray<NSNumber *> *keyTimes
来保存这些数值对应的时间点。
@property(nullable, copy) NSArray *values;
存放放关键帧(keyframe)的数组,动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧。
@property(nullable, copy) NSArray<NSNumber *> *keyTimes;
存放对应的关键帧指定的对应的时间点,存放的类型必须为NSNumber
类型,取值范围在0到0.1(会根据duration进行一个时间的换算),keyTimes里面每一个时间点对应数组values的每一帧,如果没有设置keyTimes或者KeyTimes = @[]
,那么各个关键帧的时间会被系统平分。
需要注意的是keyTimes
数组里的时间点,必须要后面的值大于前面的值,而且最好不要使用分数设置像这样@(1/7)
这样3/7
或者这样@(4/9)
因为你可能会得到一个除不尽的小数,动画效果会出现问题,不会按照你的预期执行,这个已经测试。
![](http://upload-images.jianshu.io/upload_images/276769-24f49d490299f282.gif?imageMogr2/auto-orient/strip)
keyframe
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"];
keyframeAnimation.values = @[@(WIDTH/2 - 35) , @(WIDTH/2+35) , @(WIDTH/2 - 35) , @(WIDTH/2+35) , @(WIDTH/2 - 35),@(WIDTH/2+35), @(WIDTH/2 - 35),@(WIDTH/2)];
keyframeAnimation.keyTimes = @[@0, @(0.1), @(0.2), @(0.3),@(0.4),@(0.5) ,@(0.7),@1 ];
keyframeAnimation.duration = 3;
[icon.layer addAnimation:self.keyframeAnimation forKey:@"keyfra
meAnimation"];
@property(nullable) CGPathRef path;
CAKeyframeAnimation
路径动画的路径,但是一般不直接使用它,使用的是UIBezierPath
,UIBezierPath
是CGPathRef
数据类型的封装。
UIBezierPath
UIBezierPath
的作用就是创建直线,曲线,矩形圆弧或其它复杂的曲线 形状。UIBezierPath
的使用场景有三种:
- 1 . 与
CAKeyAnimation
结合使用,作为动画的路径。 - 2 . 在方法
- (void)drawRect:(CGRect)rect
中画图使用。 - 3 . 与
CAShapeLayer
配合使用,贝塞尔曲线给CAShapeLayer
提供路径,CAShapeLayer
在提供的路径中进行渲染。
这里先看看它与CAKeyAnimation
的结合使用。
UIBezierPath 相关类方法
+ (instancetype)bezierPathWithRect:(CGRect)rect;
绘制一个矩形的贝塞尔曲线
![](http://upload-images.jianshu.io/upload_images/276769-17ac311958269662.gif?imageMogr2/auto-orient/strip)
bezierPathWithRect
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100 , (WIDTH - 100) / 2, 200)];
CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyFrameAnimation.duration = 2.0;
keyFrameAnimation.repeatCount = 3;
keyFrameAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
keyFrameAnimation.fillMode = kCAFillModeForwards;
[_icon.layer addAnimation:keyFrameAnimation forKey:@"keyFrameAnimation"];
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
绘制一个矩形的内切圆的贝塞尔曲线
![](http://upload-images.jianshu.io/upload_images/276769-72979c42987b8c37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
内切圆
![](http://upload-images.jianshu.io/upload_images/276769-9b19fa453b3e56af.gif?imageMogr2/auto-orient/strip)
内切圆.gif
UIBezierPath *bezierPath2 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, (WIDTH - 100)/2, 200)];
keyFrameAnimation.path = bezierPath2.CGPath;
[_icon.layer addAnimation:keyFrameAnimation forKey:@"keyFrameAnimation"];
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect
cornerRadius:(CGFloat)cornerRadius;
绘制一个可以设置圆角的矩形贝塞尔曲线
![](http://upload-images.jianshu.io/upload_images/276769-fb39196f7f521ea0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
bezierPathWithRoundedRect:
cornerRadius:
![](http://upload-images.jianshu.io/upload_images/276769-ef62bc07666a9087.gif?imageMogr2/auto-orient/strip)
bezierPathWithRoundedRect:
cornerRadius:
CGRect frame = CGRectMake(WIDTH/2 - 100, 150, 200, 200);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:frame cornerRadius:65];
keyFrameAnimation.path = bezierPath.CGPath;
[_icon.layer addAnimation:keyFrameAnimation forKey:@"keyFrameAnimation"];
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect
byRoundingCorners:(UIRectCorner)corners
cornerRadii:(CGSize)cornerRadii;
画一个有部分圆角矩形的贝塞尔曲线rect
:矩形的framecorners
:一个枚举值,用来指定矩形哪些角被设置为圆角,五个可选值,左上角、右上角、左下角、右下角、全部。
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};
cornerRadii:(CGSize)cornerRadii
cornerRadii 是 CGSize 类型 , 你看其它的资料大部分都写的是 这是用来设置椭圆半径的,首先这不能叫半径,要叫也是叫长轴(2a),短轴(2b),其次你设置height(高)是根本没有效果的,只有width是有效果的,这个圆角其实就是半径 = width 圆的一段圆弧 , 如下图所示,你也可以自己做个测试,也可以自己做个路径动画
![](http://upload-images.jianshu.io/upload_images/276769-c2b5d8fa8e7daa58.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
一句话总结就是height 是多少无所谓 , 类似与
layer.cornerRadius
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center
radius:(CGFloat)radius
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngle
clockwise:(BOOL)clockwise;
自己定义一些参数画一段圆弧。
center:圆弧的圆心坐标
radius:圆弧的半径
startAngle:圆弧起始的弧度
endAngle:圆弧结束的弧度
clockwise:圆弧从起始弧度指向结束弧度的方向,YES是顺时针方向,NO是逆时针方向
需要注意的startAngel endAngel并不是让你填入类似45度 , 30度这种数值,startAngel endAngel是弧度,圆一周的弧度数为2πr/r=2π,360°角=2π弧度,所以如果你想使用使用30度 , 90度这种数值请进行换算,代码里面M_PI等同于π。
![](http://upload-images.jianshu.io/upload_images/276769-c70d9b73779a2da4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
上图是iOS devlop文档的配图,看下面我做的图更好理解一点。
![](http://upload-images.jianshu.io/upload_images/276769-0992faecc25bbbf5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
下面你就可以自己画一个圆弧看一下效果。
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = ColorWithHexValue(0x5EB35F);
}
return self;
}
- (void)drawRect:(CGRect)rect
{
//设置线的填充色
[ColorWithHexValue(0xE9816D) setStroke];
//新建一个bezier对象
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:30 startAngle:0 endAngle:1.5*M_PI clockwise:YES];
//设置线宽度
bezierPath.lineWidth = 1;
//开始绘制
[bezierPath stroke];
}
图像如下所示:
![](http://upload-images.jianshu.io/upload_images/276769-bf0a5e12ab3ab325.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
构建路径Path的相关方法:
- (void)moveToPoint:(CGPoint)point
改变当前点的位置到point点,你可以看作是设置一条Path的起点。
- (void)addLineToPoint:(CGPoint)point
从当前点增加一条直线到该point,然后改变当前点把point作为当前点 。
-(void)addArcWithCenter:(CGPoint)center
radius:(CGFloat)radius
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngle
clockwise:(BOOL)clockwise
这个就跟上面说过的画一段圆弧的方法是一样的。
- (void)addCurveToPoint:(CGPoint) controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
构建一个三次的贝塞尔曲线。
- (void)addCurveToPoint:(CGPoint) controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
构建一段二次贝塞尔曲线。
贝塞尔曲线,上面时间函数的时候就已经说得比较清楚了,还想更深入的了解的话,维基百科写的很清晰,讲的也很清楚,有兴趣的可以科学的查一下。
UIBezierPath 就先到这里结束了,通常情况的具体三种用法就是一开始介绍它的时候写的,自己试试吧。
继续回到@interface CAKeyframeAnimation : CAPropertyAnimation
calculationMdel
计算模式,主要针对的动画的没一怔的内容都是一个坐标点的情况,也就是针对anchortPoint和position进行的动画,当在平面坐标系中有多个离散的点的时候可以是离散的,也可以是直线相连后进行插值计算,还可以使用圆滑的曲线将他们相连后进行差着计算,calculationMdel提供如下几种模式:
插值:在离散数据的基础上补插连续函数,使得这条连续曲线通过全部给定的离散数据点.插值是离散函数逼近的重要方法,利用它通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值,插值:用来填充图像变换时像素之间的空隙。
CA_EXTERN NSString * const kCAAnimationLinear
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
CA_EXTERN NSString * const kCAAnimationDiscrete
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
CA_EXTERN NSString * const kCAAnimationPaced
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
CA_EXTERN NSString * const kCAAnimationCubic
__OSX_AVAILABLE_STARTING (__MAC_10_7, __IPHONE_4_0);
CA_EXTERN NSString * const kCAAnimationCubicPaced
__OSX_AVAILABLE_STARTING (__MAC_10_7, __IPHONE_4_0);
kCAAnimationLinear: calculationModel的默认值,表示当关键帧是坐标点的时候,关键这之间直接直线连接进行插值计算;
kCAAnimationDiscrete: 离散的,就是不进行插值计算,所有关键帧直接逐个进行显示 ;
kCAAnimationPaced:使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timeFunctions无效;
kCAAnimationCubic对关键帧为坐标点的关键帧进行圆滑曲线相连后插值计算,曲线的形状可以通过tensionValues
,continuityValues
,biasValues
来进行调整自定义,使得运动的轨迹变得圆滑;
kCAAnimationCubicPaced:这个是基于kCAAnimationCubic的,它的作用是使动画运动变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的。
![](http://upload-images.jianshu.io/upload_images/276769-b3139a444759bf21.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
calculationMdel.png
@property(nullable, copy) NSArray<NSNumber *> *tensionValues;
@property(nullable, copy) NSArray<NSNumber *> *continuityValues;
@property(nullable, copy) NSArray<NSNumber *> *biasValues;
这三个属性是对每一帧的细节的设置,分别是张力值,持续性值,偏移值。
@property(nullable, copy) NSString *rotationMode;
rotationModel 有两个可选值:
CA_EXTERN NSString * const kCAAnimationRotateAuto
CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAAnimationRotateAutoReverse
CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
这两个都可以让做动画的时候沿着路径的切线方向,但是也有稍微的不同,看下面的演示就清楚了。
![](http://upload-images.jianshu.io/upload_images/276769-173f59186228680a.gif?imageMogr2/auto-orient/strip)
kCAAnimationRotateAuto.gif
![](http://upload-images.jianshu.io/upload_images/276769-ab2af1575b4fe1f3.gif?imageMogr2/auto-orient/strip)
kCAAnimationRotateAutoReverse.gif
@interface CASpringAnimation : CABasicAnimation
CASpringAnimation是CABasicAnimation的子类,也就是说CABasicAnimation定义的属性它也都有,看看CASpringAnimation自己独有的属性有哪些。
需要注意的是CASpringAnimation是iOS9才引入的动画类,按目前APP的开发,一般很少有 只支持iOS9以上的,一般都会兼容iOS7,个人建议使用POP。
@property CGFloat mass;
质量,影响图层运动时的弹簧惯性,质量越大,动画运动的幅度越大,弹簧拉伸压缩的幅度越大,数值必须大于0,默认是1。
@property CGFloat stiffness;
弹簧的劲度系数(弹性系数),必须大于0,默认为100。它描述的是单位形变量时所产生弹力的大小,k值大,说明形变单位长度需要的力大,或者说弹簧"韧",劲度系数在数值上等于弹簧伸长(或缩短)单位长度的弹力。
@property CGFloat damping;
阻尼系数,必须大于或者等于0,默认为10。
阻尼是指任何振动系统在振动中,由于外界作用、摩擦力等)和/或系统本身固有的原因引起的振动幅度逐渐下降的特性,以及此一特性的量化表征。
在实际振动中,由于摩擦力总是存在的,所以振动系统最初所获得的能量,在振动过程中因阻力不断对系统做负功,使得系统的能量不断减少,振动的强度逐渐减弱,振幅也就越来越小,以至于最后的停止振动,像这样的因系统的力学能,由于摩擦及转化成内能逐渐减少,振幅随时间而减弱振动,称为阻尼振动。
![](http://upload-images.jianshu.io/upload_images/276769-5d8867f525d87496.gif?imageMogr2/auto-orient/strip)
一个有阻尼的弹簧振子振动示意图
@property CGFloat initialVelocity;
连接到弹簧的对象得得初始速度,默认值是0,负值的时候,速度方向与运动方向相反。
@property(readonly) CFTimeInterval settlingDuration;
动画的结算时间,从spring动画开始到停止的一个估算时间,根据当前设置的动画参数进行估算。
CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:@"position.x"];
springAnimation.fromValue = @100;
springAnimation.toValue = @150;
springAnimation.mass = 100.0;
springAnimation.stiffness = 100.0;
springAnimation.damping = 100.0;
springAnimation.initialVelocity = 10;
springAnimation.repeatCount = 3;
springAnimation.fillMode = kCAFillModeRemoved;
[_icon.layer addAnimation:springAnimation forKey:@"springAnimation"];
![](http://upload-images.jianshu.io/upload_images/276769-fc52fc9ccd146414.gif?imageMogr2/auto-orient/strip)
springAnimation
参考列表:
https://objccn.io/issue-12-1/
https://developer.mozilla.org/zh-CN/docs/Web/CSS/timing-function
http://geeklu.com/2012/09/animation-in-ios/