zoukankan      html  css  js  c++  java
  • iOS开发之 动画CoreAnimation

    CoreAnimation 简介

    基本概念

    CoreAnimation 可用在 Mac OS X和iOS平台
    CoreAnimation 的动画执行过程都是在后台操作的,不会阻塞主线程。
    CoreAnimation 是直接作用在CALayer 上的,并不是UIView。
    用一张图来简要介绍一下核心动画成员之间的关系。
    

    1

    关于Animation 的架构

    2

    UIView 与CALayer的结构关系

        CoreAnimation 基础框架
        CALayer 图层(动画都是基于图层产生的)
        CALayer 不可以与用户交互,只能显示内容
        CALayer 发生改变,子图层 不会跟随发生改变
        UIView 是一个视图,可以添加响应事件,可以与用户交互
        UIView —>rootLayer 发生改变,子图层也会发生改变
            例如:UIButton ,不止有一个视图,改变的只是其中一个视图的rootLayer —>仍然会显示未被裁切的边框
    
    为何要封装成这么多的类?
    体现了面向对象的特性,就是每个对象都控制着不同的特性,每个类控制动画一方面。
    

    核心动画与UIView动画的区别

    核心动画的一切都是假象,并不会真是改变layer的值
    UIView 真实改变属性才能有动画
    使用场景:
        UIView 用在需要交互的地方
        核心动画用在不需要交互的地方。
    

    CAAnimation

    基础框架
    
        CALayer 图层(动画都是基于图层产生的)
        自带动画效果(当属性值改变的时候)—> 隐式动画
        UIView 是一个视图,可以添加响应事件,可以与用户交互,他的rootLayer没有动画效果
        CALayer 不可以与用户交互,只能显示内容
            bounds 边境范围
            position 中心点
            zPosition z轴中心点
            anchorPoint 锚点 ✮✮✮✮✮
                默认锚点 是与中心点重合
                锚点的值与位置
                    最小值是(0,0),最大值是(1,1)
                    默认锚点的值是(0.5,0.5)= 中心点
                    (0,0) = 图层的左上角
                    (0,1) = 图层的左下角
                    (1,0) = 图层的右上角
                    (1,1) = 图层的右下角
                    锚点值 = 锚点在视图上的位置.x.y/视图的宽高
                当视图改变的时候,是以锚点为基准改变的
            anchorPointZ Z轴锚点
            transform 转换形态
            frame NO. Animatable 坐标
            hidden 隐藏
            doubleSided 图层背面是否显示
            geometryFlipped 翻转 颠倒
            masksToBounds 裁切边境
            contents 内容
            opaque 不透明度
            allowsEdgeAntialiasing 是否使用 变形后的抗锯齿
            backgroundColor 背景颜色
            borderWidth 边框宽
            borderColor 边框颜色
            opacity 不透明度
            shadowColor 阴影颜色
            shadowOpacity 阴影不透明度
            rasterizationScale 防止Retina屏幕像素化
            shadowOffset 阴影偏移量
            shadowRadius 阴影的半径
    

    CAAnimation 子类

    关键帧动画

    CAKeyframeAnimation 简介
        关键帧动画,是CAPropertyAnimation的子类
        与 CABasicAniamtion 的区别是CABasicAniamtion是从fromValue 到toValue 两个值的变化,而 CAKeyframeAnimation 可以使用 NSArray 来保存这些数值。
    属性说明:
        values:NSArray 对象,里面的元素就是“关键帧”,动画会在指定的时间内,依次显示 values 数组中的每一个关键帧。
        path:可以设置CGPathRef、CGMutablePathRef,让图层按照轨迹移动,path只对CALayer的 anchorPoint 和 point 起作用。如果设置了path, 那么values将被忽略。
        keyTimes:关键帧指定对应的时间点,取值范围从0~1,keyTimes中的每一个时间值对应values中的每一帧,如果没有设置,时间是平分的。
        CABasicAniamtion 可看做 只有两个关键帧的CAKeyframeAnimation
    

    动画组

    CAAnimationGroup
        是 CAAnimation 的子类,可以保存一组动画对象,将 CAAnimationGroup 对象加入层之后,族中所有的动画对象可以同时并发运行。
    属性说明:
        animations:保存一组动画对象的NSArray。
        默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的 beginTimer 属性来更改动画的开始时间。
    

    转场动画

    CATransition
        为层提供移出屏幕和移入屏幕的动画效果。
        UINavigationController就是通过CATransition实现了将控制器推入屏幕的动画效果
    动画属性:
        type:动画过渡类型
        subtype:动画过渡方向
        startProgress:动画起点
        endProgress:动画终点
    
    CAAnimation (基类)所有动画的父类,不能直接使用
        CAPropertyAnimation (属性动画)也是基类,不可直接使用—>通过属性值的改变产生动画效果
            CABasicAnimation (基础动画)只能是两个点之间的变化
                CASpringAnimation
            CAKeyFrameAnimation (关键帧动画)可以添加多个点的变化 或者路径
        CAAnimationGRoup(动画组)可以同时添加多种动画,达到预期效果
        CATransition(转场动画 )给视图切换的时候 添加动画效果
    

    代码示例

    #import "ViewController.h"
    #define Angle(a) (a)*M_PI/180
    #define sAngle 6    //每秒旋转多少度:sAngle*N秒*M_PI/180
    @interface ViewController (){
        CALayer *layer;
    }
    @property (nonatomic,strong)CALayer *pointLayer;
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        //初始化CAlayer
        layer = [CALayer layer];
        layer.frame = CGRectMake(100, 100, 100, 100);
        //设置图层拐角
        layer.cornerRadius = 100/2;
        layer.backgroundColor =[UIColor brownColor].CGColor;
        //添加到父图层
        [self.view.layer addSublayer:layer];
        NSLog(@"%@",self.pointLayer);
        _pointLayer.anchorPoint =CGPointMake(0, 0.5);
        //放置一张图片
        /*
         图层上 要的是颜色(CGColorRef)  图片(CGImageRef)的 数据,而非UI
         */
        _pointLayer.contents =(id)[UIImage imageNamed:@"shizhen.png"].CGImage;
       self.pointLayer.anchorPoint = CGPointMake(0.5, 0.9);
        [self star];
        [NSTimer scheduledTimerWithTimeInterval:1 
                target:self selector:@selector(star) userInfo:nil repeats:YES];
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
        //获得点击触摸的点
        layer.position =[[touches anyObject] locationInView:self.view];
        CGFloat width =CGRectGetWidth(layer.bounds)!=100?100:50;
        //通过获得的点 改变尺寸
        layer.bounds =CGRectMake(0, 0, width, width);
        //改变颜色
        CGColorRef color = [UIColor brownColor].CGColor != layer.backgroundColor ? 
                    [UIColor brownColor].CGColor:
                    [UIColor colorWithRed:0.863 
                                green:0.634 
                                blue:0.490 
                                alpha:1.000].CGColor;
        layer.backgroundColor = color;
        //改变半径
        layer.cornerRadius =width!=100?0:width/2;
        //改变透明度
        layer.opacity = 0.3;
    
    
        //设置锚点,最大值是1,最小值是0
        _pointLayer.transform = CATransform3DMakeRotation(/*弧度*/Angle(60),0 , 0, 1);
    
    
    
    }
    -(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        layer.opacity = 1.0;
        //还原
        _pointLayer.transform = CATransform3DIdentity;
    }
    
    //利用懒加载 创建一个layer
    -(CALayer *)pointLayer{
        if (_pointLayer) {
            return _pointLayer;
        }
        _pointLayer = [CALayer layer];
        _pointLayer.bounds =CGRectMake(0, 0, 18, 220);
        //设置中心点
        _pointLayer.position =self.view.center;
        _pointLayer.backgroundColor =
                    [UIColor colorWithRed:1.000
                                green:0.392 
                                blue:0.929 
                                alpha:1.000].CGColor;
        [self.view.layer addSublayer:_pointLayer];
        return _pointLayer;
    }
    -(void)star{
        //NSCalendar 日历;可以获得年月日 时分秒,都是NSCalendar上的组件
    //    NSDateComponents
        //获得当前日历
        NSCalendar *calender =[NSCalendar currentCalendar];
        NSDateComponents *componest =[calender components:
                    NSCalendarUnitHour|
                    NSCalendarUnitMinute|
                    NSCalendarUnitSecond fromDate:[NSDate date]];
        float s = componest.second * sAngle;
        _pointLayer.transform = CATransform3DMakeRotation(/*弧度*/Angle(s),0 , 0, 1);
    }
    

    实际应用

    CABasicAnimation

    CABasicAnimation *ani3 = [CABasicAnimation animation];
      ani3.keyPath = @"opacity";
      ani3.fromValue = @1;
      ani3.toValue = @0;
      ani3.duration = 1;
      ani3.repeatCount = MAXFLOAT;
      ani3.removedOnCompletion = NO;
      ani3.fillMode = kCAFillModeBackwards;
    CAKeyframeAnimation
    

    按照路径移动

    CAKeyframeAnimation* ani = [CAKeyframeAnimation animation];
    ani.keyPath = @"position";
    UIBezierPath *path =[UIBezierPath bezierPathWithArcCenter:_img.center radius:100 startAngle:0 endAngle:M_PI*2 clockwise:YES];
    ani.path = path.CGPath;
    ani.repeatCount = MAXFLOAT;
    ani.duration = 2;
    ani.removedOnCompletion = NO;
    ani.fillMode = kCAFillModeBoth;
    

    抖动

    #define angle2Radion(angle) (angle / 180.0 * M_PI)
    CAKeyframeAnimation*ani2 = [CAKeyframeAnimation animation];
    ani2.keyPath = @"transform.rotation";
    ani2.values = @[@(angle2Radion(-5)),@(angle2Radion(5)),@(angle2Radion(-5))];
    ani2.duration = 0.15;
    ani2.repeatCount = MAXFLOAT;
    CAAnimationGroup
    /*注意:每一次group循环内的动画仍旧是由每个动画各自的属性控制,
        有一点需要注意就是duration这个属性,为了保证动画的连贯性,
        group的duration属性最好设置成与动画数组内时间最长的动画一致。*/
    //group
    CAAnimationGroup *group = [CAAnimationGroup animation];
    //位移
    CAKeyframeAnimation* ani = [CAKeyframeAnimation animation];
    ani.keyPath = @"position";
    UIBezierPath *path =[UIBezierPath bezierPathWithArcCenter:_img.center radius:100 
                startAngle:0 endAngle:M_PI*2 clockwise:YES];
    ani.path = path.CGPath;
    ani.repeatCount = MAXFLOAT;
    ani.duration = 2;
    ani.removedOnCompletion = NO;
    ani.fillMode = kCAFillModeBoth;
    //抖动
    CAKeyframeAnimation*ani2 = [CAKeyframeAnimation animation];
    ani2.keyPath = @"transform.rotation";
    ani2.values = @[@(angle2Radion(-5)),@(angle2Radion(5)),@(angle2Radion(-5))];
    ani2.duration = 0.15;
    ani2.repeatCount = MAXFLOAT;
    //透明度
    CABasicAnimation *ani3 = [CABasicAnimation animation];
    ani3.keyPath = @"opacity";
    ani3.fromValue = @1;
    ani3.toValue = @0;
    ani3.duration = 1;
    ani3.repeatCount = MAXFLOAT;
    ani3.removedOnCompletion = NO;
    ani3.fillMode = kCAFillModeBackwards;
    //将动画添加进group
    group.animations = @[ani,ani2,ani3];
    group.duration = 2;
    group.repeatCount = MAXFLOAT;
    group.removedOnCompletion = NO;
    group.fillMode = kCAFillModeBackwards;
    [_img.layer addAnimation:group forKey:nil];
    CATransition
    // 界面切换的代码
      static int i = 2;
      NSString *imageName = [NSString stringWithFormat:@"%d",i];
      _imageView.image = [UIImage imageNamed:imageName];
      i++;
      if (i > 3) {
          i = 1;
      }
      // 只要切换界面 都可以使用转场动画
      // 谁切换界面 就添加到谁上
      // 转场动画代码必须和界面切换的代码放在一起
      // 转场动画
      CATransition *anim = [CATransition animation];
      // 指定转场类型
      anim.type = @"pageCurl";
      // 设置转场的方向
      anim.subtype = kCATransitionFromLeft;
      // 设置动画的进度
      anim.startProgress = 0;
      anim.endProgress = 1;
      anim.duration = 0.5;
      [_imageView.layer addAnimation:anim forKey:nil];
    

    粒子发送器图层

        CAEmitterLayer:发送器
            birthRate:每秒发送粒子数量
            emitterMode :发送的样式
                kCAEmitterLayerPoints:点
                kCAEmitterLayerOutline:线
                kCAEmitterLayerSurface:面
                kCAEmitterLayerVolume:团
                kCAEmitterLayerRectangle 矩形
                kCAEmitterLayerCuboid 立方体
                kCAEmitterLayerCircle 曲线
                kCAEmitterLayerSphere 圆形
            renderMode渲染样式
                kCAEmitterLayerOldestFirst 最后的出生的粒子 在第一个
                kCAEmitterLayerOldestLast 最后的出生的粒子 在最后面
                kCAEmitterLayerBackToFront 把后面的 放到上面
                kCAEmitterLayerAdditive 叠加
            kCAEmitterLayerOldestFirst 最后的出生的粒子 在第一个
            kCAEmitterLayerOldestLast 最后的出生的粒子 在最后面
            kCAEmitterLayerBackToFront 把后面的 放到上面
            kCAEmitterLayerAdditive 叠加
            emitterCells:在粒子发送器上面 添加粒子
        CAEmitterCell:粒子
            contents:粒子的内容
            lifetime:存活时间
            lifetimeRange:存活时间范围
            birthRate:每秒粒子生成的数量
            emissionLatitude:散发的纬度(方向) 纬度表示 上下
            emissionLongitude:散发的经度(方向) 经度表示左右
            velocity :发送的速度(速度越快,越远)
            velocityRange:发送的速度范围
            xAcceleration:X轴加速度
            yAcceleration:y轴加速度
            zAcceleration:z轴加速度
            emissionRange:散发的范围 弧度
            name:可以通过名字 找到粒子
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    //初始化粒子发送器
        //self.emitterLayer.frame = CGRectMake(0, 300, 100,400);
        //    设置粒子发送器 每秒钟发送的数量
        self.emitterLayer.birthRate = 1;
        //设置粒子的中心点
        //self.emitterLayer.position = CGPointMake(0, 0);
        //    设置粒子发送器的样式
        self.emitterLayer.renderMode = kCAEmitterLayerBackToFront;
        //设置粒子的中心店
        self.emitterLayer.emitterPosition =self.view.center;
    
    //初始化粒子
        CAEmitterCell *cell = [CAEmitterCell emitterCell];
        cell.contents = (id)[UIImage imageNamed:@"H3"].CGImage;
        //粒子的出生量
        cell.birthRate = 10;
        //粒子的存活时间,默认是0,单位是秒
        cell.lifetime = 10;
        cell.lifetimeRange =11;
        //粒子发送的速度,默认是0
        cell.velocity = 100;
        cell.velocityRange= 10;
        //设置粒子发送的 方向
        cell.emissionLongitude = 30*M_PI/180;
        cell.emissionLatitude = M_PI/180;
        //设置粒子散发的范围 弧度
        cell.emissionRange = 180*M_PI/180;
        //设置粒子的加速度
        cell.yAcceleration = -100;
    
    // 把粒子的cell 放到粒子发送器上
        self.emitterLayer.emitterCells = @[cell];
    
    }
    
    -(CAEmitterLayer *)emitterLayer{
        if (_emitterLayer) {
            return _emitterLayer;
        }
        _emitterLayer =[CAEmitterLayer layer];
        [self.view.layer addSublayer:_emitterLayer];
        return _emitterLayer;
    }
    

    渐变图层

        CAGradientLayer
            colors:渐变颜色的数组 必须是CGColor类型
            locations: 颜色渐变的百分比 数组
            startPoint: 颜色渐变的起始点
            endPoint:颜色渐变的终点 颜色会根据设置的起始点和终点去变化
    
    self.view.backgroundColor =[UIColor whiteColor];
    UIImageView *imageView =[[UIImageView alloc]initWithFrame:self.view.frame];
    imageView.contentMode =UIViewContentModeScaleAspectFit;
    imageView.image =[UIImage imageNamed:@"H4"];
    [self.view addSubview:imageView];
    //初始化渐变 图层
    CAGradientLayer *layer =[CAGradientLayer layer];
    layer.frame =self.view.frame;
    //设置渐变的颜色数组
    layer.colors =@[(id)[UIColor redColor].CGColor,
                    (id)[UIColor orangeColor].CGColor,
                    (id)[UIColor yellowColor].CGColor,
                    (id)[UIColor greenColor].CGColor,
                    (id)[UIColor cyanColor].CGColor,
                    (id)[UIColor blueColor].CGColor,
                    (id)[UIColor purpleColor].CGColor];
    //设置透明度
    layer.opacity =0.2;
    //设置起始点
    layer.startPoint  = CGPointMake(0, 0);
    //设置
    layer.locations =@[@0.1,@0.2,@0.3];
    [self.view.layer addSublayer:layer];
    

    ![3][http://down.treney.com/img/CAAnimation/ca3.png]

    复制图层 CAReplicatorLayer

    在使用复制图层的时候,必须重写layerClass,把原有的CALayer 修改成 CAReplicatorLayer
    
    //创建重写后 可以复制的图层
    CanReplicatorLayer *view= [[CanReplicatorLayer alloc]initWithFrame:
            CGRectMake(0, 100, 300, 300)];
    [self.view addSubview:view];
    
    self.view.backgroundColor =[UIColor whiteColor];
    UIImageView *imageView =[[UIImageView alloc]initWithFrame:
            CGRectMake(0, 100, 300, 300)];
    imageView.contentMode =UIViewContentModeScaleAspectFit;
    imageView.image =[UIImage imageNamed:@"H4"];
    [view addSubview:imageView];
    
    //复制图层
    CAReplicatorLayer *layer = (CAReplicatorLayer *)view.layer;
    //instance 复制之后,有原来的图层  有新的图层,这是现在实例化出来的layer的数量
    layer.instanceCount = 2;//复制出来一份
    //改变复制图层的样式
    CATransform3D tansFrom = CATransform3DMakeTranslation(0, 80, 0);
    layer.instanceTransform = CATransform3DRotate(tansFrom,180*M_PI/180, 1, 0, 0);
    //设置复制图层 颜色偏移量
    layer.instanceRedOffset =-0.2;
    layer.instanceBlueOffset = -0.3;
    layer.instanceGreenOffset =-0.4;
    

    效果图

    4

    作者:【鹏威の博客】 treney
    本文链接:http://blog.treney.com/index.php/archives/CoreAnimation.html?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
    来源博客:http://blog.treney.com/
  • 相关阅读:
    node.js--express学习之路(三)
    node.js--express学习之路(二)
    node.js--express学习之路(一)
    js简单实现获取验证码倒计时
    HTML5新特性postMessage解决跨域
    跨域通信—JSONP
    前端跨域的那些事
    前后端通信—CORS(支持跨域)
    前后端通信—webSocket(支持跨域)
    JavaScript的运行机制
  • 原文地址:https://www.cnblogs.com/liuxiaokun/p/5548467.html
Copyright © 2011-2022 走看看