在了解核心动画之前需要先了解CALayer的基本知识,因为CAAnimation的实现是基于CALayer之上。(不了解的童鞋可查阅资料)
CAMediaTiming协议
CALayer和CAAnimation都遵守CAMediaTiming 协议,所以CAAnimation在使用过程中协议属性的设置至关重要。
CAMediaTiming协议属性如下(已做注释):
@protocol CAMediaTiming
//对象的开始时间,指动画开始之前的的时间。默认是0.
@property CFTimeInterval beginTime;
//对象的持续时间,默认是0.
@property CFTimeInterval duration;
//图层的比率,用来衡量最初时间和当前时间。例如:如果比率为2,当前花费的时间就是最初时间的0.5。默认是1。
@property float speed;
/*时间轴偏移量。将时间轴移动至偏移位置,再执行整个动画时长。假设动画时长5秒,偏移量为13,
则开始位置为13 % 5 = 3,再执行一次动画时长5秒,即在时长位置3处结束。*/
@property CFTimeInterval timeOffset;
//重复次数
@property float repeatCount;
//对象的重复持续时间。默认是0。
@property CFTimeInterval repeatDuration;
//是否自动换向。如果为YES,那么动画执行完会按照原来的路径返回。默认是NO。
@property BOOL autoreverses;
/*决定当前对象过了非active时间段的行为. 比如动画开始之前、动画结束之后。
如果是CAAnimationd对象,则需要将其removedOnCompletion设置为NO,要不然fillMode不起作用
*/
@property(copy) NSString *fillMode;
@end
补充:
fillMode值类型 | 注明 |
---|---|
kCAFillModeRemoved | 默认值。动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态 |
kCAFillModeForwards | 动画结束后,layer会一直保持着动画最后的状态 |
kCAFillModeBackwards | 动画开始前,只要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始 |
kCAFillModeBoth | 动画开始之前,layer处于动画初始状态,动画结束后layer保持动画最后的状态 |
CAAnimation
CAAnimation主要分为:转场动画(CATransition)、关键帧动画(CAKeyframeAnimation)、基本动画(CABasicAnimation)、组合动画(CAAnimationGroup)。
(为了更容易看懂每个动画之间的关系,整理了CAAnimation层级结构图。)
CAAnimation的属性以及方法对继承自它的四种动画类型是通用的,下面是CAAnimation的属性列表。
/*速度控制函数*/
@property(nullable, strong) CAMediaTimingFunction *timingFunction;
/*动画代理。会在动画开始和结束时调用对应代理方法。*/
@property(nullable, strong) id <CAAnimationDelegate> delegate;
/*动画执行完毕后是否从图层上移除。默认是YES*/
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;
补充:
timingFunction类型 | 注明 |
---|---|
kCAMediaTimingFunctionLinear(线性) | 匀速状态 |
kCAMediaTimingFunctionEaseIn(渐入) | 缓慢进入,然后加速离开 |
kCAMediaTimingFunctionEaseOut(渐出) | 全速进入,然后减速到达最终位置 |
kCAMediaTimingFunctionEaseInEaseOut(渐进渐出) | 缓慢进入,中间加速,最后减速到达最终位置。默认类型 |
下面具体看下每一种动画类型如何使用,以及各自的使用场景。
1、转场动画(CATransition)
CATransition类用来实现layer的转场过渡动画。
首先需要了解下CATransition的属性(已做备注)。
@interface CATransition : CAAnimation
/* 过渡动画名。当前合法的动画类型有:`fade', `moveIn', `push' and `reveal'. 默认是`fade'. */
@property(copy) NSString *type;
/* 一个可选择的动画子类型. 用来指定动画的运动方向,合法的值是: `fromLeft', `fromRight', `fromTop' and * `fromBottom'. */
@property(nullable, copy) NSString *subtype;
/* 这两个属性用来控制过渡动画的开始、结束执行过程,可以让动画停留在某个动画点上。合法的值是在0~1范围内。endProgress必须比startProgress的值要大。默认值是0—1.
*/
@property float startProgress;
@property float endProgress;
/*filter对象用来实现过渡动画。如果设置了filter,那么为layer设置的type和subtype属性将被忽略。*/
@property(nullable, strong) id filter;
@end
补充:
type类型还有很多私有API效果,如果代码中使用的话可能会导致app审核不通过。
基本效果
fade //交叉淡化过渡
push //新视图把旧视图推出去
moveIn //新视图移到旧视图上面
reveal //将旧视图移开,显示下面的新视图
私有API:
cube //立方体翻滚效果
oglFlip //上下左右翻转效果
suckEffect //收缩(不支持过渡方向)
rippleEffect //滴水(不支持过渡方向)
pageCurl //向上翻页
pageUnCurl //向下翻页
cameraIrisHollowOpen //相机镜头打开(不支持过渡方向)
cameraIrisHollowClose //相机镜头关上(不支持过渡方向)
设置type的代码不在此处展示,只需要修改type类型即可实现。下面看下不同type的动画效果。(因为gif图大小限制,所以公有和私有API效果分开展示)
基本API运行效果:
私有API的运行效果如下:
现在通过代码实例来看一下StartProgress\EndProgress的使用。
注:为了能清楚看出设置Progress的效果,我把duration时间设置为5s。通过对比能够看出来设置StartProgress、EndProgress前后的区别,可根据自己的实际需要进行设置。
UILabel * label1 = [[UILabel alloc]init];
label1.backgroundColor = [UIColor lightGrayColor];
label1.text = @"label1";
label1.textColor = [UIColor blackColor];
[self.view addSubview:label1];
UILabel * label2 = [[UILabel alloc]init];
label2.text = @"label2";
label2.backgroundColor = [UIColor lightGrayColor];
label2.textColor = [UIColor blackColor];
[self.view addSubview:label2];
//label1的的动画
CATransition *animation1 = [CATransition animation];
animation1.duration = 5.0f;
animation1.type = kCATransitionPush;
animation1.subtype = kCATransitionFromBottom;//过渡方向
[animation1 setStartProgress:0.5];//设置动画起点(匀速过渡动画路径的50%处开始)
[animation1 setEndProgress:0.8];//设置动画终点(匀速过渡动画路径的80%处结束)
[label1.layer addAnimation:animation1 forKey:@"animation"];
//label2的的动画
CATransition *animation2 = [CATransition animation];
animation2.duration = 5.0f;
animation2.type = kCATransitionPush;
animation2.subtype = kCATransitionFromBottom;
[label2.layer addAnimation:animation2 forKey:@"animation"];
[label1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@20);
make.top.equalTo(@180);
make.width.height.mas_equalTo(100);
}];
[label2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(@-20);
make.top.equalTo(@180);
make.width.height.mas_equalTo(100);
}];
运行结果如下:
2、基本动画(CABasicAnimation)
CAPropertyAnimation是通过animationWithKeyPath方法来进行实例化的,所以CABasicAnimation、CAKeyframeAnimation都可以通过animationWithKeyPath的值来设定动画样式。
animationWithKeyPath常用值如下:
KeyPath | 效果 |
---|---|
transform.scale | 整体比例转换 |
transform.scale.x | 宽的比例转换 |
transform.scale.y | 高的比例转换 |
transform.rotation.x | 围绕x轴旋转 |
transform.rotation.y | 围绕y轴旋转 |
transform.rotation.z | 围绕z轴旋转(平面效果是围绕中心点旋转 ) |
transform.translation.x | 沿x方向位置移动 |
transform.translation.y | 沿y方向位置移动 |
transform.translation.z | 沿z方向位置移动 |
position | 位置移动(关键帧动画使用时可以不设置方向,用贝塞尔曲线绘制路径) |
position.x | 沿x方向位置移动 |
position.y | 沿y方位置向移动 |
position.z | 沿z方向位置移动 |
bounds | 坐标大小转换 |
opacity | 改变透明度 |
CABasicAnimation的属性列表(已做备注):
@interface CABasicAnimation : CAPropertyAnimation
@property(nullable, strong) id fromValue;//所改变属性的起始值
@property(nullable, strong) id toValue;//所改变属性的结束时的值
@property(nullable, strong) id byValue;//所改变属性相同起始值的改变量
@end
CABasicAnimation三种动画方式的实现代码示例:
//移动动画
case 0:
{
CABasicAnimation *Animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
Animation.duration = 1.0f;//动画时长
Animation.fromValue = @(imageView.center.y-30);// 起始帧
Animation.toValue = @(imageView.center.y+50);//终止帧
Animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];//速度控制方式:匀速线性
Animation.repeatCount = 1;//重复次数
Animation.removedOnCompletion = NO;
Animation.fillMode = kCAFillModeForwards;//动画结束后,layer会一直保持着动画最后的状态
[self.view.layer addAnimation:Animation forKey:@"AnimationMoveY"];
}
break;
//旋转动画:x、y、z三个方向
case 1:{
CABasicAnimation *Animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];//围绕Y轴方向旋转
Animation.duration = 1.0f;
Animation.repeatCount = 1;
Animation.fromValue = @(0);
Animation.toValue = @(M_PI);//旋转一周
Animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[imageView.layer addAnimation:Animation forKey:@"RotationY"];
}
break;
// 缩放动画:x、y、z
case 2:{
CABasicAnimation *Animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; // 设定为缩放
Animation.duration = 1.5f;
Animation.repeatCount = 1;
Animation.autoreverses = YES; // 动画结束时执行逆动画,回到起始帧
Animation.fromValue = [NSNumber numberWithFloat:1.0]; // 开始时的倍率
Animation.toValue = [NSNumber numberWithFloat:2.0]; // 结束时的倍率
// Animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
// Animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.3, 0.3, 1.0)];
[imageView.layer addAnimation:Animation forKey:@"scale-layer"];
}
break;
//平移动画:必须设定方向
case 3:{
CABasicAnimation *Animation=[CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
Animation.duration = 1;
Animation.repeatCount = 0;
Animation.removedOnCompletion = FALSE;
Animation.fillMode = kCAFillModeForwards;
Animation.autoreverses = YES;
Animation.fromValue = [NSNumber numberWithFloat:0];
Animation.toValue = [NSNumber numberWithFloat:60];
[self.view.layer addAnimation:Animation forKey:@"animateLayer"];
}
break;
运行效果如下:
这只是基本动画的简单实现,稍微复杂的动画可以通过借助组合动画实现,后面会有介绍。
3、关键帧动画(CAKeyframeAnimation)
CAKeyframeAnimation的属性列表(已做备注)。
@interface CAKeyframeAnimation : CAPropertyAnimation
/*存储关键帧(keyframe)元素。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧。*/
@property(nullable, copy) NSArray *values;
/*设置CGPathRef\CGMutablePathRef,让layer跟着设定的路径移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,values将被忽略。*/
@property(nullable) CGPathRef path;
/*为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平均分配的。*/
@property(nullable, copy) NSArray<NSNumber *> *keyTimes;
/*速度控制函数,有kCAMediaTimingFunctionLinear、kCAMediaTimingFunctionEaseIn、kCAMediaTimingFunctionEaseOut、kCAMediaTimingFunctionEaseInEaseOut四种方式,默认是最后一种*/
@property(nullable, copy) NSArray<CAMediaTimingFunction *> *timingFunctions
/*计算模式。主要针对的是每一帧的内容为一个座标点的情况,也就是对anchorPoint 和 position 进行的动画。
目前提供如下几种模式:kCAAnimationLinear、kCAAnimationDiscrete 、kCAAnimationPaced
、kCAAnimationCubic 、kCAAnimationCubicPaced。*/
@property(copy) NSString *calculationMode;
/* 该值控制着曲线的紧密度(正值将越紧,负值将越宽松)*/
@property(nullable, copy) NSArray<NSNumber *> *tensionValues;
/*该值控制片段之间的链接(正值将有锋利的圆角,负值将是倒立的圆角)*/
@property(nullable, copy) NSArray<NSNumber *> *continuityValues;
/*该值定义了曲线发生的地点(正值将在控制点前移动曲线,负值将在控制点后移动)*/
@property(nullable, copy) NSArray<NSNumber *> *biasValues;
/*定义是否沿着路径旋转匹配对象动画路径切线,值可能为kCAAnimationRotateAuto和kCAAnimationRotateAutoReverse。默认是nil。如果没有路径对象,设置该属性值将无效,kCAAnimationRotateAutoReverse为了匹配正切将添加180°。*/
@property(nullable, copy) NSString *rotationMode;
@end
下面是对calculationMode的几种模式的具体注释:
calculationMode | 注释 |
---|---|
kCAAnimationLinear | 线性。关键帧为坐标点时,直接直线相连进行插值计算。 |
kCAAnimationDiscrete | 离散不连续的。只展示关键帧的状态,没有中间过程,没有动画。 |
kCAAnimationPaced | 节奏。动画均匀进行,不按keyTimes设置的或者按关键帧平分时间,所以keyTimes和timingFunctions无效 |
kCAAnimationCubic | 以关键帧为坐标点进行圆滑曲线相连后插值计算,目的是使得运行的轨迹变得圆滑。对于曲线的形状还可以通过tensionValues、continuityValues、biasValues进行调整自定义 |
kCAAnimationCubicPaced | 在kCAAnimationCubic的基础上使动画变得均匀,就是系统时间内运动的距离相同。如果设定的话keyTimes、timingFunctions无效。 |
注:
CAKeyframeAnimation和CABasicAnimation都是CApropertyAnimation的子类,区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation可以使用values保存这些数值,但是CAKeyframeAnimation的实现方式也不止values,还有path。
实现关键帧动画两种方式的代码示例(values和path):
-(void)AnimationClick:(UIButton *)button{
switch (button.tag) {
//Values实现
case 0:
{
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//动画路径的values
NSValue *value1=[NSValue valueWithCGPoint:CGPointMake(100, 250)];
NSValue *value2=[NSValue valueWithCGPoint:CGPointMake(200, 250)];
NSValue *value3=[NSValue valueWithCGPoint:CGPointMake(200, 300)];
NSValue *value4=[NSValue valueWithCGPoint:CGPointMake(100, 300)];
NSValue *value5=[NSValue valueWithCGPoint:CGPointMake(100, 400)];
NSValue *value6=[NSValue valueWithCGPoint:CGPointMake(200, 400)];
animation.values=@[value1,value2,value3,value4,value5,value6];
//重复次数
animation.repeatCount=MAXFLOAT;
//设置是否原路返回
animation.autoreverses = YES;
//设置移动速度,越小越快
animation.duration = 4.0f;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[imageView.layer addAnimation:animation forKey:nil];
}
break;
//通过Path方式
case 1:{
//创建动画对象
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//创建一个CGPathRef对象,就是动画的路线
CGMutablePathRef path = CGPathCreateMutable();
//设置开始位置
CGPathMoveToPoint(path,NULL,100,250);
//沿直线移动
CGPathAddLineToPoint(path,NULL, 200, 250);
CGPathAddLineToPoint(path,NULL, 200, 300);
CGPathAddLineToPoint(path,NULL, 100, 300);
CGPathAddLineToPoint(path,NULL, 100, 400);
CGPathAddLineToPoint(path,NULL, 200, 400);
//沿着曲线移动
CGPathAddCurveToPoint(path,NULL,200.0,500.0,250.0,500.0,250.0,400.0);
CGPathAddCurveToPoint(path,NULL,250.0,500.0,300.0,500.0,300.0,400.0);
//自动沿着弧度移动
CGPathAddEllipseInRect(path, NULL, CGRectMake(300, 400, 70, 70));
animation.path=path;
CGPathRelease(path);
animation.autoreverses = YES;
animation.repeatCount=1;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.duration = 8.0f;
animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[imageView.layer addAnimation:animation forKey:nil];
//绘制出动画路径,方便观察
CAShapeLayer *line = [[CAShapeLayer alloc]init];
line.fillColor = [UIColor clearColor].CGColor;
line.borderWidth = 1;
line.strokeColor = [UIColor redColor].CGColor;
line.path = path;
[self.view.layer addSublayer:line];
}
break;
default:
break;
}
}
运行结果如下:
4、组合动画(CAAnimationGroup)
CAAnimationGroup只有一个属性animations。
@interface CAAnimationGroup : CAAnimation
/* 存储CAAnimation对象的数组。数组的每一个元素在规定的duration时间内同时进行。*/
@property(nullable, copy) NSArray<CAAnimation *> *animations;
@end
做一个简单的加入购物车动画。代码如下::
//贝塞尔曲线路径
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:CGPointMake(0.0, 64.0)];
[movePath addQuadCurveToPoint:CGPointMake(300, 400) controlPoint:CGPointMake(200, 100)];
//关键帧动画(设置位置)
CAKeyframeAnimation * posAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
posAnim.path = movePath.CGPath;
//缩放动画
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.3, 0.3, 1.0)];
//旋转动画
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];//围绕Y轴方向旋转
rotationAnimation.duration = 0.2;
rotationAnimation.repeatCount = MAXFLOAT;
rotationAnimation.fromValue = @(0);
rotationAnimation.toValue = @(M_PI);//旋转一周
//透明度动画
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.6];
//动画组
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:posAnim, scaleAnimation,rotationAnimation, opacityAnim,nil];
animGroup.duration = 1;
animGroup.autoreverses = NO;
animGroup.fillMode = kCAFillModeForwards;
[goodsView.layer addAnimation:animGroup forKey:nil];
运行效果: