美文网首页Ios
iOS动画(1):Core Animation核心动画

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

作者: 程序猿的产品狗进化史 | 来源:发表于2017-02-13 23:29 被阅读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

    相关文章

      网友评论

        本文标题:iOS动画(1):Core Animation核心动画

        本文链接:https://www.haomeiwen.com/subject/gsngittx.html