iOS動畫之CAShapeLayer(一)提交動畫

checkButton1.gif

今天動畫的主要用CAShapeLayer和貝塞爾曲線做一個提交的動畫,也是沒有什么難度的

先簡單的介紹下CAShapeLayer

  1. CAShapeLayer繼承自CALayer,可使用CALayer的所有屬性
  2. CAShapeLayer需要和貝塞爾曲線配合使用才有意義。
    Shape:形狀,貝塞爾曲線可以為其提供形狀,而單獨使用CAShapeLayer是沒有任何意義的。
  3. 使用CAShapeLayer與貝塞爾曲線可以實現(xiàn)不在view的DrawRect方法中畫出一些想要的圖形

關(guān)于CAShapeLayer和DrawRect的比較

DrawRect:DrawRect屬于CoreGraphic框架,占用CPU,消耗性能大
CAShapeLayer:CAShapeLayer屬于CoreAnimation框架,通過GPU來渲染圖形,節(jié)省性能。動畫渲染直接提交給手機GPU,不消耗內(nèi)存

貝塞爾曲線與CAShapeLayer的關(guān)系

  1. CAShapeLayer中shape代表形狀的意思,所以需要形狀才能生效
  2. 貝塞爾曲線可以創(chuàng)建基于矢量的路徑
  3. 貝塞爾曲線給CAShapeLayer提供路徑,CAShapeLayer在提供的路徑中進行渲染。路徑會閉環(huán),所以繪制出了Shape
  4. 用于CAShapeLayer的貝塞爾曲線作為Path,其path是一個首尾相接的閉環(huán)的曲線,即使該貝塞爾曲線不是一個閉環(huán)的曲線

以上文字來源于網(wǎng)絡(luò),哈哈

如果有同學(xué)對UIBezierPath不熟悉的請看這里

CAShapeLayer的介紹

上面的介紹也說了貝塞爾曲線給CAShapeLayer提供路徑,CAShapeLayer在提供的路徑中進行渲染,把路徑用以形狀的形式展示出來,CAShapeLayer最重要的屬性就是下面三個:

 //動畫的路徑
@property(nullable) CGPathRef path;

//描述path路徑從哪里開始
@property CGFloat strokeStart;
//描述path路徑從哪里結(jié)束
@property CGFloat strokeEnd;
 這兩個值的范圍是[0,1],

接下來我們先畫一個不會動的 對號 來學(xué)習(xí)一下 CAShapeLayer

UIBezierPath *bezierPath=[UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(self.frame.size.width/4, self.frame.size.height/2)];
[bezierPath addLineToPoint:CGPointMake(self.frame.size.width/2, self.frame.size.height/4*3)];
[bezierPath addLineToPoint:CGPointMake(self.frame.size.width/4*3, self.frame.size.height/3)];


CAShapeLayer *shape=[CAShapeLayer layer];


  shape.lineWidth=17;
  shape.fillColor=[UIColor clearColor].CGColor;
  shape.strokeColor=[UIColor colorWithRed:0.76f green:0.89f blue:0.89f alpha:1.00f].CGColor;
  shape.lineCap = kCALineCapRound;
  shape.lineJoin = kCALineJoinRound;

  shape.path=bezierPath.CGPath;
  [self.layer addSublayer:shape];

UIBezierPath只是告訴路徑給CAShapeLayer,具體這個shape什么樣子由CAShapeLayer來決定
所以一些屬于lineWidth,fillColor是在shape上設(shè)置的,在UIBezierPath上設(shè)置無效

補充:lineCap

  • kCALineCapButt: 默認格式,不附加任何形狀;
  • kCALineCapRound: 在線段頭尾添加半徑為線段 lineWidth 一半的半圓;
  • kCALineCapSquare: 在線段頭尾添加半徑為線段 lineWidth 一半的矩形”
checkButton

CAShapeLayer的基本用法就是這樣接下來就是動畫了

動畫第一步長方形變圓形

先添加一個checkButton的cornerRadius變?yōu)閳A形的高度一半的動畫

注意
我們知道,使用 CAAnimation 如果不做額外的操作,動畫會在結(jié)束之后返回到初始狀態(tài)。或許你會這么設(shè)置:

radiusAnimation.fillMode = kCAFillModeForwards; radiusAnimation.removedOnCompletion = NO;

但這不是正確的方式。正確的做法可以參考 WWDC 2011 中的 session 421 - Core Animation Essentials。
Session 中推薦的做法是先顯式地改變 Model Layer 的對應(yīng)屬性,再應(yīng)用動畫。這樣一來,我們甚至省去了 toValue。
因為 cornerRadius 也是 Animatable 的,所以可以作為 KeyPath 進行動畫。首先顯式地設(shè)定屬性的終止?fàn)顟B(tài),為進度條高度的 1/2 : ZMButtonSize().height/2. 設(shè)置好起始狀態(tài)。

static CGSize ZMButtonSize() {
return CGSizeMake(100, 100);
}
  • self.layer.cornerRadius=ZMButtonSize().height/2;
    CABasicAnimation *cornerRadiusAnimation=[CABasicAnimation animationWithKeyPath:@"cornerRadius"];
    cornerRadiusAnimation.delegate=self;
    cornerRadiusAnimation.duration=0.2;
    cornerRadiusAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    cornerRadiusAnimation.fromValue=@(self.frame.size.height/2);

    [self.layer addAnimation:cornerRadiusAnimation forKey:@"cornerRadiusAnimation"];

在Animation的代理方法里動畫一開始讓checkButton的bounds改變

-(void)animationDidStart:(CAAnimation *)anim
{

   if([[self.layer animationForKey:@"cornerRadiusAnimation"] isEqual:anim])
 {
    
    [UIView animateWithDuration:0.6f delay:0.0f usingSpringWithDamping:0.6 initialSpringVelocity:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
        self.bounds = CGRectMake(0, 0, ZMButtonSize().height, ZMButtonSize().height);
        self.backgroundColor=[UIColor colorWithRed:1.00f green:0.80f blue:0.56f alpha:1.00f];
    } completion:^(BOOL finished) {
        [self.layer removeAllAnimations];
        
        [self checkAnimation];
        
    }];

 }

}

動畫第二步畫會動的對號

在checkButton的bounds改變完成是在checkButton上畫一個對號

這時候就用到了CAShapeLayer的 strokeStart, strokeEnd,在動畫時設(shè)置KeyPath:為"strokeEnd",從0到1,這樣一個動畫的對號就出來了。

CAShapeLayer *shape=[CAShapeLayer layer];


UIBezierPath *bezierPath=[UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(self.frame.size.width/4, self.frame.size.height/2)];
[bezierPath addLineToPoint:CGPointMake(self.frame.size.width/2, self.frame.size.height/4*3)];
[bezierPath addLineToPoint:CGPointMake(self.frame.size.width/4*3, self.frame.size.height/3)];



//UIBezierPath只是告訴路徑給CAShapeLayer,具體這個shpe什么樣子由CAShapeLayer來決定
//所以一些屬于lineWidth,fillColor是在shpe上設(shè)置的,在UIBezierPath上設(shè)置無效

shape.lineWidth=17;
shape.fillColor=[UIColor clearColor].CGColor;
shape.strokeColor=[UIColor colorWithRed:0.76f green:0.89f blue:0.89f alpha:1.00f].CGColor;
shape.lineCap = kCALineCapRound;
shape.lineJoin = kCALineJoinRound;

shape.path=bezierPath.CGPath;
[self.layer addSublayer:shape];


CABasicAnimation *checkAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
checkAnimation.duration = 0.5f;
checkAnimation.fromValue = @(0.0f);
checkAnimation.toValue = @(1.0f);
checkAnimation.delegate = self;
[shape addAnimation:checkAnimation forKey:@"checkAnimation"];

理論上,所有描線的動畫你都可以用這種方式先指定一個 path 然后改變 strokeEnd, strokeStart 來實現(xiàn)。

如果感覺這篇文章對您有所幫助,順手點個喜歡,謝謝啦
代碼放在了GitHub上大家可以下載。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容