今天動(dòng)畫的主要用CAShapeLayer和貝塞爾曲線做一個(gè)提交的動(dòng)畫,也是沒有什么難度的
先簡單的介紹下CAShapeLayer
- CAShapeLayer繼承自CALayer,可使用CALayer的所有屬性
- CAShapeLayer需要和貝塞爾曲線配合使用才有意義。
Shape:形狀,貝塞爾曲線可以為其提供形狀,而單獨(dú)使用CAShapeLayer是沒有任何意義的。 - 使用CAShapeLayer與貝塞爾曲線可以實(shí)現(xiàn)不在view的DrawRect方法中畫出一些想要的圖形
關(guān)于CAShapeLayer和DrawRect的比較
DrawRect:DrawRect屬于CoreGraphic框架,占用CPU,消耗性能大
CAShapeLayer:CAShapeLayer屬于CoreAnimation框架,通過GPU來渲染圖形,節(jié)省性能。動(dòng)畫渲染直接提交給手機(jī)GPU,不消耗內(nèi)存
貝塞爾曲線與CAShapeLayer的關(guān)系
- CAShapeLayer中shape代表形狀的意思,所以需要形狀才能生效
- 貝塞爾曲線可以創(chuàng)建基于矢量的路徑
- 貝塞爾曲線給CAShapeLayer提供路徑,CAShapeLayer在提供的路徑中進(jìn)行渲染。路徑會(huì)閉環(huán),所以繪制出了Shape
- 用于CAShapeLayer的貝塞爾曲線作為Path,其path是一個(gè)首尾相接的閉環(huán)的曲線,即使該貝塞爾曲線不是一個(gè)閉環(huán)的曲線
以上文字來源于網(wǎng)絡(luò),哈哈
如果有同學(xué)對(duì)UIBezierPath不熟悉的請(qǐng)看這里
CAShapeLayer的介紹
上面的介紹也說了貝塞爾曲線給CAShapeLayer提供路徑,CAShapeLayer在提供的路徑中進(jìn)行渲染,把路徑用以形狀的形式展示出來,CAShapeLayer最重要的屬性就是下面三個(gè):
//動(dòng)畫的路徑
@property(nullable) CGPathRef path;
//描述path路徑從哪里開始
@property CGFloat strokeStart;
//描述path路徑從哪里結(jié)束
@property CGFloat strokeEnd;
這兩個(gè)值的范圍是[0,1],
接下來我們先畫一個(gè)不會(huì)動(dòng)的 對(duì)號(hào) 來學(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,具體這個(gè)shape什么樣子由CAShapeLayer來決定
所以一些屬于lineWidth,fillColor是在shape上設(shè)置的,在UIBezierPath上設(shè)置無效
補(bǔ)充:lineCap
- kCALineCapButt: 默認(rèn)格式,不附加任何形狀;
- kCALineCapRound: 在線段頭尾添加半徑為線段 lineWidth 一半的半圓;
- kCALineCapSquare: 在線段頭尾添加半徑為線段 lineWidth 一半的矩形”
CAShapeLayer的基本用法就是這樣接下來就是動(dòng)畫了
動(dòng)畫第一步長方形變圓形
先添加一個(gè)checkButton的cornerRadius變?yōu)閳A形的高度一半的動(dòng)畫
注意
我們知道,使用 CAAnimation 如果不做額外的操作,動(dòng)畫會(huì)在結(jié)束之后返回到初始狀態(tài)。或許你會(huì)這么設(shè)置:
radiusAnimation.fillMode = kCAFillModeForwards; radiusAnimation.removedOnCompletion = NO;
但這不是正確的方式。正確的做法可以參考 WWDC 2011 中的 session 421 - Core Animation Essentials。
Session 中推薦的做法是先顯式地改變 Model Layer 的對(duì)應(yīng)屬性,再應(yīng)用動(dòng)畫。這樣一來,我們甚至省去了 toValue。
因?yàn)?cornerRadius 也是 Animatable 的,所以可以作為 KeyPath 進(jìn)行動(dòng)畫。首先顯式地設(shè)定屬性的終止?fàn)顟B(tài),為進(jìn)度條高度的 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的代理方法里動(dòng)畫一開始讓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];
}];
}
}
動(dòng)畫第二步畫會(huì)動(dòng)的對(duì)號(hào)
在checkButton的bounds改變完成是在checkButton上畫一個(gè)對(duì)號(hào)
這時(shí)候就用到了CAShapeLayer的 strokeStart, strokeEnd,在動(dòng)畫時(shí)設(shè)置KeyPath:為"strokeEnd",從0到1,這樣一個(gè)動(dòng)畫的對(duì)號(hào)就出來了。
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,具體這個(gè)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"];
理論上,所有描線的動(dòng)畫你都可以用這種方式先指定一個(gè) path 然后改變 strokeEnd, strokeStart 來實(shí)現(xiàn)。
如果感覺這篇文章對(duì)您有所幫助,順手點(diǎn)個(gè)喜歡,謝謝啦
代碼放在了GitHub上大家可以下載。