• iOS 动画篇 (二) CAShapeLayer与CoreAnimation结合使用


      接上一篇博客 iOS 动画篇(一) Core Animation

      CAShapeLayer是CALayer的一个子类,使用这个类能够很轻易实现曲线的动画。

      先来一个折线动画效果:

      

    示例代码:

    //1.生成path
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(0, 0)];
        [path addLineToPoint:CGPointMake(50, 50)];
        [path addLineToPoint:CGPointMake(70, 150)];
        [path addLineToPoint:CGPointMake(100, 100)];
        [path addLineToPoint:CGPointMake(150, 130)];
        [path addLineToPoint:CGPointMake(170, 200)];
        
        self.shapeLayer.path = path.CGPath;
        
        //设置animation
        CABasicAnimation *strokeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        strokeAnimation.fromValue = @0;
        strokeAnimation.toValue = @1;
        strokeAnimation.duration = 5.f;
        
        
        CABasicAnimation *lineWidthAnimation = [CABasicAnimation animationWithKeyPath:@"lineWidth"];
        lineWidthAnimation.fromValue = @1;
        lineWidthAnimation.toValue = @5;
        lineWidthAnimation.duration = 5.f;
        
        
        CABasicAnimation *strokeColorAnimation = [CABasicAnimation animationWithKeyPath:@"strokeColor"];
        strokeColorAnimation.fromValue = (id)([UIColor redColor].CGColor);
        strokeColorAnimation.toValue = (id)([UIColor magentaColor].CGColor);
        strokeColorAnimation.duration = 5.f;
        
        CAAnimationGroup *group = [CAAnimationGroup animation];
        group.animations = @[strokeAnimation, lineWidthAnimation, strokeColorAnimation];
        group.duration = 5.f;
        group.fillMode = kCAFillModeForwards;
        group.removedOnCompletion = NO;
        [self.shapeLayer addAnimation:group forKey:@"groupAnimation"];

      现在介绍CAShapeLayer,CAShapeLayer几乎所有的属性都可以用来做动画,比如说path、strokeEnd、strokeStart、lineWidth等等,利用这些属性可以实现多种曲线动画。

      接下来,介绍一个CAShapeLayer与贝塞尔曲线结合的曲线动画,效果图:

      

    代码:

    //二次贝塞尔曲线
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(0, self.shapeLayer.bounds.size.height / 2)];
        [path addCurveToPoint:CGPointMake(self.shapeLayer.bounds.size.width, 100) controlPoint1:CGPointMake(50, 0) controlPoint2:CGPointMake(150, 200)];
        self.shapeLayer.path = path.CGPath;
        
        //绘制动画
        CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        strokeEndAnimation.fromValue = @0.5;
        strokeEndAnimation.toValue = @1;
        strokeEndAnimation.duration = 5.f;
        
        [self.shapeLayer addAnimation:strokeEndAnimation forKey:@"strokeAnimation"];
        
        CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
        strokeStartAnimation.fromValue = @0.5;
        strokeStartAnimation.toValue = @0;
        strokeStartAnimation.duration = 5.f;
        
        [self.shapeLayer addAnimation:strokeStartAnimation forKey:@"strokeStartAnimation"];

      再来一个看着酷一点的loading动画,效果:

     

    代码如下:

    self.shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
        self.shapeLayer.strokeColor = [UIColor redColor].CGColor;
        self.shapeLayer.fillColor = [UIColor clearColor].CGColor;
        self.shapeLayer.lineWidth = 5.f;
        UIBezierPath *storkePath = [UIBezierPath bezierPathWithOvalInRect:self.shapeLayer.bounds];
        self.shapeLayer.path = storkePath.CGPath;
        self.shapeLayer.strokeStart = 0;
        self.shapeLayer.strokeEnd = 0.1;
    
        //旋转动画
        CABasicAnimation *rotateAnimaiton = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        rotateAnimaiton.duration = 1.f;
        rotateAnimaiton.repeatCount = CGFLOAT_MAX;
        rotateAnimaiton.removedOnCompletion = NO;
        rotateAnimaiton.fillMode = kCAFillModeForwards;
        rotateAnimaiton.toValue = @(M_PI * 2);
        
        //stroke动画
        CABasicAnimation *storkeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        storkeAnimation.duration = 2.f;
        storkeAnimation.repeatCount = CGFLOAT_MAX;
        storkeAnimation.fillMode =  kCAFillModeForwards;
        storkeAnimation.removedOnCompletion = NO;
        storkeAnimation.toValue = @(1);
        
        CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
        animationGroup.duration = 2.f;
        animationGroup.repeatCount =CGFLOAT_MAX;
        animationGroup.fillMode = kCAFillModeForwards;
        animationGroup.removedOnCompletion = NO;
        animationGroup.animations = @[rotateAnimaiton, storkeAnimation];
        animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
        
        [self.shapeLayer addAnimation:animationGroup forKey:@"indicatorAnimation"];

      现在我们来看一个CAShapeLayer与mask结合的动画

      

    代码:

    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
        self.shapeLayer.mask = shapeLayer;
        
        UIBezierPath *fromPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 200, 0)];
        UIBezierPath *toPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 200, 200)];
        shapeLayer.path = fromPath.CGPath;
        
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
        animation.fromValue = (id)fromPath.CGPath;
        animation.toValue = (id)toPath.CGPath;
        animation.duration = 5.f;
        animation.removedOnCompletion = NO;
        animation.fillMode = kCAFillModeForwards;
        
        [shapeLayer addAnimation:animation forKey:@"animation"];

      最后再介绍一个登录动画:

      分析:这个登录动画一共分为三步

      1. 在button上添加一个shapeLayer,用path属性实现layer的展开动画

      2. 在展开动画结束后,为button设置一个shapeLayer的mask,利用layer的path和opacity属性实现收起按钮动画

      3. 添加一个loading动画到view上

    详情见代码:

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSLog(@"一个复杂一点的登录动画");
        [self.shapeLayer removeFromSuperlayer];
        
        UIButton *startButton = ({
            UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
            btn.backgroundColor = [UIColor purpleColor];
            [btn setTitle:@"start" forState:UIControlStateNormal];
            btn.frame = (CGRect){{0, 0}, {200, 50}};
            btn.center = self.view.center;
            
            [btn addTarget:self action:@selector(startAction:) forControlEvents:UIControlEventTouchUpInside];
            btn;
        });
        
        [self.view addSubview:startButton];
        
        self.startButton = startButton;
        
        
    }
    
    - (IBAction)startAction:(UIButton *)sender {
        [self addMaskAnimation];
    }
    
    - (void)addMaskAnimation
    {
        CAShapeLayer *shapeLayer = [CAShapeLayer new];
        shapeLayer.frame = self.startButton.bounds;
        shapeLayer.fillColor = [UIColor whiteColor].CGColor;
        shapeLayer.strokeColor = [UIColor whiteColor].CGColor;
        shapeLayer.opacity = .3f;
        shapeLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(self.startButton.bounds.size.width / 2, 0, 1, self.startButton.bounds.size.height)].CGPath;//不初始化则无动画效果
        
        [self.startButton.layer addSublayer:shapeLayer];
        
        
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
        animation.duration = 0.5f;
        animation.toValue = (__bridge id)[UIBezierPath bezierPathWithRect:self.startButton.bounds].CGPath;
        animation.fillMode = kCAFillModeForwards;
        animation.removedOnCompletion = NO;
        
        [shapeLayer addAnimation:animation forKey:@"shapeAnimation"];
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self addPackupAnimation];
        });
    }
    
    - (void)addPackupAnimation
    {
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.frame = self.startButton.bounds;
        self.startButton.layer.mask = maskLayer;
        
        //path动画
        CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
        pathAnimation.duration = 0.3f;
        pathAnimation.removedOnCompletion = NO;
        pathAnimation.toValue = (__bridge id)[UIBezierPath bezierPathWithArcCenter:CGPointMake(self.startButton.bounds.size.width / 2, self.startButton.bounds.size.height / 2) radius:1 startAngle:0 endAngle:M_PI * 2 clockwise:YES].CGPath;
        pathAnimation.fromValue = (__bridge id)[UIBezierPath bezierPathWithArcCenter:CGPointMake(self.startButton.bounds.size.width / 2, self.startButton.bounds.size.height / 2) radius:self.startButton.bounds.size.width / 2 startAngle:0 endAngle:M_PI * 2 clockwise:YES].CGPath;
        pathAnimation.fillMode = kCAFillModeForwards;
        
        //透明度动画
        CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
        opacityAnimation.duration = 0.3f;
        opacityAnimation.toValue = @(1);
        opacityAnimation.fromValue = @(0);
        opacityAnimation.removedOnCompletion = YES;
        opacityAnimation.fillMode = kCAFillModeForwards;
        
        CAAnimationGroup *group = [CAAnimationGroup new];
        group.animations = @[pathAnimation];
        group.removedOnCompletion = NO;
        group.fillMode = kCAFillModeForwards;
        group.duration = pathAnimation.duration;
        
        [maskLayer addAnimation:group forKey:@"packupAnimation"];
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            self.startButton.hidden = YES;
            [self addLoadingAnimation];
            
        });
    }
    
    - (void)addLoadingAnimation
    {
        CAShapeLayer *shapeLayer =  ({
            CAShapeLayer *layer = [CAShapeLayer layer];
            layer.position = self.view.center;
            layer.bounds = CGRectMake(0, 0, 50, 50);
            layer.backgroundColor = [UIColor clearColor].CGColor;
            layer.strokeColor = [UIColor redColor].CGColor;
            layer.fillColor = [UIColor clearColor].CGColor;
            layer.lineWidth = 5.f;
            UIBezierPath *storkePath = [UIBezierPath bezierPathWithOvalInRect:layer.bounds];
            layer.path = storkePath.CGPath;
            layer.strokeStart = 0;
            layer.strokeEnd = 0.1;
            
            layer;
        });
        
        [self.view.layer addSublayer:shapeLayer];
        
        //旋转动画
        CABasicAnimation *rotateAnimaiton = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        rotateAnimaiton.duration = 1.f;
        rotateAnimaiton.repeatCount = CGFLOAT_MAX;
        rotateAnimaiton.removedOnCompletion = NO;
        rotateAnimaiton.fillMode = kCAFillModeForwards;
        rotateAnimaiton.toValue = @(M_PI * 2);
        
        //stroke动画
        CABasicAnimation *storkeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        storkeAnimation.duration = 2.f;
        storkeAnimation.repeatCount = CGFLOAT_MAX;
        storkeAnimation.fillMode =  kCAFillModeForwards;
        storkeAnimation.removedOnCompletion = NO;
        storkeAnimation.toValue = @(1);
        
        CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
        animationGroup.duration = 2.f;
        animationGroup.repeatCount =CGFLOAT_MAX;
        animationGroup.fillMode = kCAFillModeForwards;
        animationGroup.removedOnCompletion = NO;
        animationGroup.animations = @[rotateAnimaiton, storkeAnimation];
        animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
        
        [shapeLayer addAnimation:animationGroup forKey:@"indicatorAnimation"];
    }

      核心动画就介绍到这,你可以在这里查看demo。

      个人原创,转载请注明出处 http://www.cnblogs.com/pretty-guy/p/8268745.html

      下一篇博客打算介绍利用CADisplayLink与CoreGraphics结合实现动画

  • 相关阅读:
    Preliminaries for Benelux Algorithm Programming Contest 2019: I. Inquiry I
    Preliminaries for Benelux Algorithm Programming Contest 2019:A:Architecture
    pta刷题:L1-008 求整数段和 (10分)记录总结
    关系代数复习ing
    操作系统学习笔记:银行家算法浅谈
    mysql学习笔记:复习ing
    mysql学习笔记:集合运算并交差,其他
    JZOJ1900. 【2010集训队出题】矩阵
    【2019暑假】08.14比赛总结
    【2019暑假集训】08.13比赛总结
  • 原文地址:https://www.cnblogs.com/pretty-guy/p/8268745.html
Copyright © 2020-2023  润新知