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結合實現動畫


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM