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结合实现动画

转载于:https://www.cnblogs.com/pretty-guy/p/8268745.html

猜你喜欢

转载自blog.csdn.net/weixin_34357887/article/details/93438607