UIButton自定義路徑動畫
- 歡迎同樣喜歡動效的工程師/UI設計師/產品加入我們
- iOS動效特攻隊–>QQ群:547897182
- iOS動效特攻隊–>熊熊:648070256
之前看了一個別人做的漢堡動畫的動效,非常有意思,然後在花瓣網上找了一個差不多的,自己嘗試著做了一下。
花瓣網上找的動效
說一下,這個動效主要用的就是路徑動效,都是在CGPath上操作的。蝦面開始講解制作過程。
準備工作
開動!
1,用Sketch繪製圖形,一個圓圈和裡面的勾,注意,勾和圓圈的路徑要連起來的。具體的Sketch使用教程就不說了,自己問度娘。我也是自己慢慢摸索出來的。
實在不會的我這裡有一個我做好的sketch檔案,下載下來直接開啟就行,可以參考一下
2,將Sketch繪製的圖形以SVG格式匯出,Sketch介面右下角就能看到
3,將SVG檔案拖到PaintCode,就會自動生成路徑程式碼
4,現在開始程式碼部分
先新建一個UIButton類,先貼出程式碼,程式碼不是很多,蝦面開始一一解釋
#import "CheckBtn.h"
#import "CALayer+CheckBtnLayer.h"
static CGFloat checkStrokeStart = 0.038;
static CGFloat checkStrokeEnd = 0.195 ;
static CGFloat circleStrokeStart = 0.28;
static CGFloat circleStrokeEnd = 1;
@interface CheckBtn ()
{
CAShapeLayer *checkShapeLayer;
}
@end
@implementation CheckBtn
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (!self) {
self = nil ;
}
self.layer.borderWidth = 3.0f;
self.layer.cornerRadius = self.frame.size.width/2.0;
self.layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3].CGColor;
checkShapeLayer = [CAShapeLayer layer];
checkShapeLayer.path = [self checkPath];
checkShapeLayer.lineWidth = 3.0f;
checkShapeLayer.lineCap = kCALineCapRound;
checkShapeLayer.lineJoin = kCALineJoinRound;
checkShapeLayer.strokeColor = [UIColor whiteColor].CGColor;
checkShapeLayer.fillColor = [UIColor clearColor].CGColor;
checkShapeLayer.actions = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNull null],@"strokeStart",[NSNull null],@"strokeEnd", nil];
[self.layer addSublayer:checkShapeLayer];
self.showCheck = NO;
return self;
}
@synthesize showCheck = _showCheck;
- (void)setShowCheck:(BOOL)showCheck
{
_showCheck = showCheck;
CABasicAnimation *strokeStart = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
CABasicAnimation *strokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
if (self.showCheck) {
strokeStart.toValue = [NSNumber numberWithFloat:checkStrokeStart];
strokeStart.duration = 0.4;
strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :0.4 :0.3 :1.6];
strokeEnd.toValue = [NSNumber numberWithFloat:checkStrokeEnd];
strokeEnd.duration = 0.5;
strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.15 :0.6 :0.6 :1];
}else{
strokeStart.toValue = [NSNumber numberWithFloat:circleStrokeStart];
strokeStart.duration = 0.4;
strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.4 :0.58 :0.88];
strokeEnd.toValue = [NSNumber numberWithFloat:circleStrokeEnd];
strokeEnd.duration = 0.55;
strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.08 :0.64 :0.1];
}
[checkShapeLayer ocb_applyAnimation:strokeStart];
[checkShapeLayer ocb_applyAnimation:strokeEnd];
}
- (CGPathRef)checkPath
{
UIBezierPath* oval1DrawPath = UIBezierPath.bezierPath;
[oval1DrawPath moveToPoint: CGPointMake(33.85, 11.21)];
[oval1DrawPath addLineToPoint: CGPointMake(16.5, 26.07)];
[oval1DrawPath addLineToPoint: CGPointMake(2.95, 10.33)];
[oval1DrawPath addCurveToPoint: CGPointMake(3.57, 9.3) controlPoint1: CGPointMake(2.95, 10.33) controlPoint2: CGPointMake(3.21, 9.86)];
[oval1DrawPath addCurveToPoint: CGPointMake(4.33, 8.2) controlPoint1: CGPointMake(3.77, 8.98) controlPoint2: CGPointMake(4.04, 8.59)];
[oval1DrawPath addCurveToPoint: CGPointMake(6.12, 6.11) controlPoint1: CGPointMake(4.87, 7.45) controlPoint2: CGPointMake(5.59, 6.62)];
[oval1DrawPath addCurveToPoint: CGPointMake(8.53, 4.08) controlPoint1: CGPointMake(6.69, 5.54) controlPoint2: CGPointMake(7.62, 4.72)];
[oval1DrawPath addCurveToPoint: CGPointMake(10.6, 2.79) controlPoint1: CGPointMake(9.59, 3.33) controlPoint2: CGPointMake(10.6, 2.79)];
[oval1DrawPath addCurveToPoint: CGPointMake(28.09, 2.65) controlPoint1: CGPointMake(10.6, 2.79) controlPoint2: CGPointMake(18.78, -2.09)];
[oval1DrawPath addCurveToPoint: CGPointMake(36.36, 28.09) controlPoint1: CGPointMake(37.4, 7.39) controlPoint2: CGPointMake(41.1, 18.78)];
[oval1DrawPath addCurveToPoint: CGPointMake(10.91, 36.36) controlPoint1: CGPointMake(31.61, 37.4) controlPoint2: CGPointMake(20.22, 41.1)];
[oval1DrawPath addCurveToPoint: CGPointMake(0.71, 21.82) controlPoint1: CGPointMake(5.18, 33.44) controlPoint2: CGPointMake(1.47, 27.88)];
[oval1DrawPath addCurveToPoint: CGPointMake(0.59, 18.86) controlPoint1: CGPointMake(0.59, 20.84) controlPoint2: CGPointMake(0.55, 19.85)];
[oval1DrawPath addCurveToPoint: CGPointMake(1.06, 15.25) controlPoint1: CGPointMake(0.64, 17.66) controlPoint2: CGPointMake(0.78, 16.45)];
[oval1DrawPath addCurveToPoint: CGPointMake(1.79, 12.78) controlPoint1: CGPointMake(1.09, 15.12) controlPoint2: CGPointMake(1.36, 13.92)];
[oval1DrawPath addCurveToPoint: CGPointMake(2.91, 10.39) controlPoint1: CGPointMake(2.22, 11.67) controlPoint2: CGPointMake(2.9, 10.41)];
CGPathRef pathRef = oval1DrawPath.CGPath;
return pathRef;
}
4.1 建立半透明白色圓環作為基底
self.layer.borderWidth = 3.0f;
self.layer.cornerRadius = self.frame.size.width/2.0;
self.layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3].CGColor;
4.2 新建一個CAShaperLayer物件CAShapeLayer *checkShapeLayer;
配置checkShapeLayer的引數
checkShapeLayer = [CAShapeLayer layer];
checkShapeLayer.path = [self checkPath]; //這裡的checkPath就是之前我們生成的path路徑
checkShapeLayer.lineWidth = 3.0f;//線寬
checkShapeLayer.lineCap = kCALineCapRound;//layer邊緣的樣式
checkShapeLayer.lineJoin = kCALineJoinRound;//layer銜接部分的樣式
checkShapeLayer.strokeColor = [UIColor whiteColor].CGColor;//layer描邊的顏色,
checkShapeLayer.fillColor = [UIColor clearColor].CGColor;//layer填充的顏色,注意,這和stroke不一樣
checkShapeLayer.actions = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNull null],@"strokeStart",[NSNull null],@"strokeEnd", nil];//layer增加動畫事件
[self.layer addSublayer:checkShapeLayer];
4.3 後面設定按鈕點選動畫
4.3.1 這四個引數分別是“Check”和“Circle”這兩種不同形狀時的strokeStart和strokeEnd的位置,有效值為0~1
static CGFloat checkStrokeStart = 0.038;
static CGFloat checkStrokeEnd = 0.195;
static CGFloat circleStrokeStart = 0.28;
static CGFloat circleStrokeEnd = 1;
4.3.2 建立CABasicAnimation strokeStart和strokeEnd,為什麼 @”strokeStart”和@”strokeEnd”能建立動畫,官方文件上說了,這兩個屬性是Animation的,可以建立動畫的,建議大家看看CAShapeLayer的官方文件,不長,而且容易看懂。
strokeStart.toValue 動畫的最終位置
strokeStart.duration 動畫之行的時長
strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :0.4 :0.3 :1.6];動畫的步速
這是一種通過貝塞爾曲線的方式來調整速度的方式,比如先快後慢,或者先慢後快。看下面的文章就容易理解了。
參考文章
http://netcetera.org/camtf-playground.html
https://isux.tencent.com/ios-easing-tween-animation.html
@synthesize showCheck = _showCheck;
- (void)setShowCheck:(BOOL)showCheck
{
_showCheck = showCheck;
CABasicAnimation *strokeStart = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
CABasicAnimation *strokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
if (self.showCheck) {
strokeStart.toValue = [NSNumber numberWithFloat:checkStrokeStart];
strokeStart.duration = 0.4;
strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :0.4 :0.3 :1.6];
strokeEnd.toValue = [NSNumber numberWithFloat:checkStrokeEnd];
strokeEnd.duration = 0.5;
strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.15 :0.6 :0.6 :1];
}else{
strokeStart.toValue = [NSNumber numberWithFloat:circleStrokeStart];
strokeStart.duration = 0.4;
strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.4 :0.58 :0.88];
strokeEnd.toValue = [NSNumber numberWithFloat:circleStrokeEnd];
strokeEnd.duration = 0.55;
strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.08 :0.64 :0.1];
}
[checkShapeLayer ocb_applyAnimation:strokeStart];
[checkShapeLayer ocb_applyAnimation:strokeEnd];
}
在CALayer的基礎上擴充套件的一個方法,
animation.fromValue = [self.presentationLayer valueForKey:animation.keyPath];
從動畫演示層presentationLayer
獲取之前的狀態,並且設定為fromValue
再設定toValue,和animation開始執行動畫
- (void)ocb_applyAnimation:(CABasicAnimation *)animation
{
if (animation.fromValue == nil) {
animation.fromValue = [self.presentationLayer valueForKey:animation.keyPath];
}
[self addAnimation:animation forKey:animation.keyPath];
[self setValue:animation.toValue forKey:animation.keyPath];
}
這樣,一個有趣的動效就完成了。做的過程中遇到不懂的問度娘或者看官方文件都行,官方文件對這一塊的解釋都比較清楚,能看懂。歡迎有同樣喜歡動效的程式設計師和設計師加入我。QQ:648070256