Ios

iOS动画(1):Core Animation核心动画

2017-02-13  本文已影响335人  程序猿的产品狗进化史

在了解核心动画之前需要先了解CALayer的基本知识,因为CAAnimation的实现是基于CALayer之上。(不了解的童鞋可查阅资料)

CAMediaTiming协议

CALayerCAAnimation都遵守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结构图.png
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效果.gif

私有API的运行效果如下:

私有API效果展示.gif

现在通过代码实例来看一下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);
        
    }];
   

运行结果如下:

Transition.gif

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;


运行效果如下:


BasicAnimation.gif

这只是基本动画的简单实现,稍微复杂的动画可以通过借助组合动画实现,后面会有介绍。

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无效。

注:
CAKeyframeAnimationCABasicAnimation都是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;
    }
}


运行结果如下:

KeyFrameAnimation.gif

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];

运行效果:

GroupAnimation.gif
上一篇下一篇

猜你喜欢

热点阅读