天天看點

iOS個人整理38-貝塞爾曲線和幀動畫結合

一、貝塞爾曲線

參考來自下面的文章,這篇文章被轉了很多次,原文都不見了

http://www.cnblogs.com/moyunmo/p/3600091.html?utm_source=tuicool&utm_medium=referral

首先說貝塞爾的各種劃線方法

1.利用UIbezier的初始化方法,在UIView上畫bezierPath

a.利用UIbezier的初始化方法,可以建立出圓形,矩形,圓角矩形

b.使用moveToPoint設定起始點,使用addLineToPoint增加點

下面的類繼承于UIView,當此CircleView添加到父視圖上時,會自動調用drawRect方法

//弧度轉角度
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
//角度轉弧度
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)

#import "CircleView.h"

@implementation CircleView


-(void)drawRect:(CGRect)rect
{
    
    //1.圓形
    UIBezierPath *bPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(300, 300) radius:50 
                          startAngle: DEGREES_TO_RADIANS(135) endAngle:M_PI*2 clockwise:YES];
 
    //設定顔色
    [[UIColor redColor]setStroke];
    //設定線寬
    [bPath setLineWidth:5];
    //繪制
    [bPath stroke];
    
    //2.橢圓
    UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(200, 150, 100, 200)];
    [ovalPath setLineWidth:5];
    [ovalPath stroke];
    
    //3.矩形
    UIBezierPath *myBezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, 100, 50)];
    
    [[UIColor whiteColor]setStroke];
    [myBezierPath setLineWidth:5];
    [myBezierPath stroke];
    
   
    
    
    //4.圓角矩形
    //UIRectCorner可以設定 哪幾個角是圓角,其他不變
    UIBezierPath *tBPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(220, 20, 100, 100) 
                           byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomLeft cornerRadii:CGSizeMake(20, 20)];
    
    [[UIColor greenColor]setStroke];
    [tBPath setLineWidth:5];
    [tBPath stroke];
    
    
    
    //5.通過添加點生成任意圖形
    
    UIBezierPath* aPath = [UIBezierPath bezierPath];
    aPath.lineWidth = 15.0;
    
    aPath.lineCapStyle = kCGLineCapButt;  //線條終點
    //round 圓形
    //butt 平的 預設值 把線連接配接到精準的終點
    //Square 平的,會把線延伸到終點再加上線寬的一半
    aPath.lineJoinStyle = kCGLineJoinBevel;  //拐點處理
    //bevel 斜角斜面,角的外側是平的不圓滑
    //miter 斜接 角的外側是尖的
    //round 圓角
    
    //這是起點
    [aPath moveToPoint:CGPointMake(100.0, 200.0)];
    
    //添加點
    [aPath addLineToPoint:CGPointMake(200.0, 240.0)];
    [aPath addLineToPoint:CGPointMake(160, 340)];
    [aPath addLineToPoint:CGPointMake(40.0, 340)];
    [aPath addLineToPoint:CGPointMake(10.0, 240.0)];
    [aPath closePath]; //第五條線通過調用closePath方法得到的
    
    [aPath stroke]; //Draws line 根據坐标點連線
    
}
@end
           

2.二次曲線和三次曲線

盜圖兩張,他們解釋了控制點是怎麼回事

iOS個人整理38-貝塞爾曲線和幀動畫結合
iOS個人整理38-貝塞爾曲線和幀動畫結合

劃線方法很簡單

二次曲線

//建立一條貝塞爾
    UIBezierPath* aPath = [UIBezierPath bezierPath];
    
    aPath.lineWidth = 5.0;//寬度
    aPath.lineCapStyle = kCGLineCapRound;  //線條拐角
    aPath.lineJoinStyle = kCGLineJoinRound;  //終點處理
    //起始點
    [aPath moveToPoint:CGPointMake(20, 100)];
    //添加兩個控制點
    [aPath addQuadCurveToPoint:CGPointMake(220, 100) controlPoint:CGPointMake(170, 0)];
    //劃線
    [aPath stroke];
    
           

三次曲線

//三次曲線
    
    UIBezierPath* bPath = [UIBezierPath bezierPath];
    
    
    bPath.lineWidth = 5.0;
    bPath.lineCapStyle = kCGLineCapRound;  //線條拐角
    bPath.lineJoinStyle = kCGLineCapRound;  //終點處理
    //起始點
    [bPath moveToPoint:CGPointMake(20, 250)];
    
    //添加兩個控制點
    [bPath addCurveToPoint:CGPointMake(350, 250) controlPoint1:CGPointMake(310, 200) controlPoint2:CGPointMake(210, 400)];
    
    [bPath stroke];
           

3.了解一下底層的Core Graphics

這篇文章說的夠了

http://www.mamicode.com/info-detail-841887.html

-(void)drawRect:(CGRect)rect
{
    // Create the path data
    //建立路徑時間
    CGMutablePathRef cgPath = CGPathCreateMutable();


    //cgPath的畫圖接口
    //給一個cgPath裡面添加了多個樣式,圓和橢圓會發生關聯
    
    //兩個橢圓互不影響
    CGPathAddEllipseInRect(cgPath, NULL, CGRectMake(100, 100, 50, 100));
    
    CGPathAddEllipseInRect(cgPath, NULL, CGRectMake(250, 250, 100, 50));
    
    //矩形
    CGPathAddRect(cgPath, NULL, CGRectMake(200, 500, 30, 100));
    
//    圓形
//    CGPathAddArc(cgPath, NULL, 120, 400, 100, 0, M_PI*2, YES);
    
    //下面兩句要搭配,先有起點
    CGPathMoveToPoint(cgPath, NULL, 200, 300);
    //加一段弧
    CGPathAddArcToPoint(cgPath, NULL, 320, 250, DEGREES_TO_RADIANS(150), M_PI*2, 50);
   
    //把CGPath賦給貝塞爾曲線
    UIBezierPath* aPath = [UIBezierPath bezierPath];
    
    aPath.CGPath = cgPath;
    
    aPath.usesEvenOddFillRule = YES;
    
   //并不在ARC的管理範圍之内。是以需要手動釋放對象,釋放cgPath
    CGPathRelease(cgPath);
    
    //劃線
    [[UIColor redColor]setStroke];
    [aPath setLineWidth:5];
    [aPath stroke];
}
           

4.通過shapeLayer畫線

這樣就不用去UIView的drawRect方法裡面畫圖了,也就是可以在ViewController裡面繪制了

//ShapeLayer
-(void)layerAnimation
{
    //貝塞爾畫圓
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:100 startAngle:0 endAngle:M_PI clockwise:NO];
    
    //初始化shapeLayer
    self.myShapeLayer = [CAShapeLayer layer];
    _myShapeLayer.frame = _redView.bounds;

    _myShapeLayer.strokeColor = [UIColor greenColor].CGColor;//邊沿線色 
    _myShapeLayer.fillColor = [UIColor grayColor].CGColor;//填充色
    
    _myShapeLayer.lineJoin = kCALineJoinMiter;//線拐點的類型
    _myShapeLayer.lineCap = kCALineCapSquare;//線終點
    
    
    //從貝塞爾曲線獲得形狀
    _myShapeLayer.path = path.CGPath;
    
    //線條寬度
    _myShapeLayer.lineWidth = 10;
    
    //起始和終止
    _myShapeLayer.strokeStart = 0.0;
    _myShapeLayer.strokeEnd = 1.0;
    
    
    //将layer添加進圖層
    [self.redView.layer addSublayer:_myShapeLayer];
       
}
           

二、關鍵幀動畫

//關鍵幀動畫
-(void)layerKeyFrameAnimation
{
    //畫一個path
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(-40, 100)];
    [path addLineToPoint:CGPointMake(360, 100)];
    [path addLineToPoint:CGPointMake(360, 200)];
    [path addLineToPoint:CGPointMake(-40, 200)];
    [path addLineToPoint:CGPointMake(-40, 300)];
    [path addLineToPoint:CGPointMake(360, 300)];
    
    //幾個固定點
    NSValue *orginalValue = [NSValue valueWithCGPoint:self.redView.layer.position];
    NSValue *value_1 = [NSValue valueWithCGPoint:CGPointMake(300, 300)];
    NSValue *value_2 = [NSValue valueWithCGPoint:CGPointMake(400, 300)];
    NSValue *value_3 = [NSValue valueWithCGPoint:CGPointMake(400, 400)];
    
    //變動的屬性,keyPath後面跟的屬性是CALayer的屬性
    CAKeyframeAnimation *keyFA = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    
    //value數組,放所有位置資訊,如果設定path,此項會被忽略
    keyFA.values = @[orginalValue,value_1,value_2,value_3];
    //動畫路徑
//    keyFA.path = path.CGPath;
    //該屬性是一個數組,用以指定每個子路徑(AB,BC,CD)的時間。如果你沒有顯式地對keyTimes進行設定,則系統會預設每條子路徑的時間為:ti=duration/(幀數),即每條子路徑的duration相等
    keyFA.keyTimes = @[@(0.0),@(0.5),@(0.9),@(2)];
    //動畫總時間
    keyFA.duration = 5.0f;
    //重複次數,小于0無限重複
    keyFA.repeatCount = 10;
    
    /*
    這個屬性用以指定時間函數,類似于運動的加速度
    kCAMediaTimingFunctionLinear//線性
    kCAMediaTimingFunctionEaseIn//淡入
    kCAMediaTimingFunctionEaseOut//淡出
    kCAMediaTimingFunctionEaseInEaseOut//淡入淡出
    kCAMediaTimingFunctionDefault//預設
     */
    keyFA.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    
   /*
    fillMode的作用就是決定目前對象過了非active時間段的行為. 比如動畫開始之前,動畫結束之後。如果是一個動畫CAAnimation,則需要将其removedOnCompletion設定為NO,要不然fillMode不起作用.
    
    下面來講各個fillMode的意義
    kCAFillModeRemoved 這個是預設值,也就是說當動畫開始前和動畫結束後,動畫對layer都沒有影響,動畫結束後,layer會恢複到之前的狀态
    kCAFillModeForwards 當動畫結束後,layer會一直保持着動畫最後的狀态
    kCAFillModeBackwards 這個和kCAFillModeForwards是相對的,就是在動畫開始前,你隻要将動畫加入了一個layer,layer便立即進入動畫的初始狀态并等待動畫開始.你可以這樣設定測試代碼,将一個動畫加入一個layer的時候延遲5秒執行.然後就會發現在動畫沒有開始的時候,隻要動畫被加入了layer,layer便處于動畫初始狀态
    kCAFillModeBoth 了解了上面兩個,這個就很好了解了,這個其實就是上面兩個的合成.動畫加入後開始之前,layer便處于動畫初始狀态,動畫結束後layer保持動畫最後的狀态.
        //添加動畫
    */
    keyFA.fillMode = kCAFillModeForwards;
    
    /*
     在關鍵幀動畫中還有一個非常重要的參數,那便是calculationMode,計算模式.該屬性決定了物體在每個子路徑下是跳着走還是勻速走,跟timeFunctions屬性有點類似
     其主要針對的是每一幀的内容為一個座标點的情況,也就是對anchorPoint 和 position 進行的動畫.當在平面座标系中有多個離散的點的時候,可以是離散的,也可以直線相連後進行插值計算,也可以使用圓滑的曲線将他們相連後進行插值計算. calculationMode目前提供如下幾種模式

     kCAAnimationLinear calculationMode的預設值,表示當關鍵幀為座标點的時候,關鍵幀之間直接直線相連進行插值計算;
     kCAAnimationDiscrete 離散的,就是不進行插值計算,所有關鍵幀直接逐個進行顯示;
     kCAAnimationPaced 使得動畫均勻進行,而不是按keyTimes設定的或者按關鍵幀平分時間,此時keyTimes和timingFunctions無效;
     kCAAnimationCubic 對關鍵幀為座标點的關鍵幀進行圓滑曲線相連後插值計算,對于曲線的形狀還可以通過tensionValues,continuityValues,biasValues來進行調整自定義,這裡的數學原理是Kochanek–Bartels spline,這裡的主要目的是使得運作的軌迹變得圓滑;
     kCAAnimationCubicPaced 看這個名字就知道和kCAAnimationCubic有一定聯系,其實就是在kCAAnimationCubic的基礎上使得動畫運作變得均勻,就是系統時間内運動的距離相同,此時keyTimes以及timingFunctions也是無效的.
     */
    keyFA.calculationMode = kCAAnimationPaced;
    
    //旋轉的模式,auto就是沿着切線方向動,autoReverse就是轉180度沿着切線動
    keyFA.rotationMode = kCAAnimationRotateAuto;

    //結束後是否移除動畫
    keyFrameAnimation.removedOnCompletion = NO;

     //添加動畫
    [self.redView.layer addAnimation:keyFA forKey:@""];
}
           

這裡有個泡泡動畫的demo,結合了貝塞爾曲線和幀動畫,很精緻

https://github.com/bnb173yjx/BubbleAnimationDemo