一、貝塞爾曲線
參考來自下面的文章,這篇文章被轉了很多次,原文都不見了
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.二次曲線和三次曲線
盜圖兩張,他們解釋了控制點是怎麼回事
劃線方法很簡單
二次曲線
//建立一條貝塞爾
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