一、貝塞爾曲線簡(jiǎn)介:
貝塞爾曲線是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學(xué)曲線。貝茲曲線由線段與節(jié)點(diǎn)組成,節(jié)點(diǎn)是可拖動(dòng)的支點(diǎn),線段像可伸縮的皮筋。它通過(guò)控制曲線上的四個(gè)點(diǎn)(起始點(diǎn)、終止點(diǎn)以及兩個(gè)相互分離的中間點(diǎn))來(lái)創(chuàng)造、編輯圖形。其中起重要作用的是位于曲線中央的控制線。這條線是虛擬的,中間與貝塞爾曲線交叉,兩端是控制端點(diǎn)。移動(dòng)兩端的端點(diǎn)時(shí)貝塞爾曲線改變曲線的曲率(彎曲的程度);移動(dòng)中間點(diǎn)(也就是移動(dòng)虛擬的控制線)時(shí),貝塞爾曲線在起始點(diǎn)和終止點(diǎn)鎖定的情況下做均勻移動(dòng)。
在iOS中,使用UIBezierPath類(lèi)完成貝塞爾曲線的繪制,UIBezierPath可以創(chuàng)建基于矢量的路徑。這個(gè)類(lèi)繼承于NSObject,在UIKit中它是基于Core Graphics對(duì)CGPathRef數(shù)據(jù)類(lèi)型和path繪圖屬性的一個(gè)封裝,需要圖形上下文(CGContextRef),所以一般UIBezierPath在drawRect中使用(當(dāng)然也有例外~)。
二、屬性方法:
1、以下方法均為類(lèi)方法創(chuàng)建UIBezierPath的方法
+ (instancetype)bezierPath;
// 根據(jù)CGRect繪制矩形;
+ (instancetype)bezierPathWithRect:(CGRect)rect;
// 根據(jù)CGRect繪制內(nèi)切橢圓(若rect是正方形,那就是內(nèi)切圓~)
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
// 設(shè)置可自定義圓角大小的圓角矩形
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
// 設(shè)置指定角是圓弧的矩形
/*
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0, // 左上角
UIRectCornerTopRight = 1 << 1, // 右上角
UIRectCornerBottomLeft = 1 << 2, // 左下角
UIRectCornerBottomRight = 1 << 3, // 右下角
UIRectCornerAllCorners = ~0UL // 全部四個(gè)角
};
*/
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
// 根據(jù)中心圓點(diǎn)、半徑、開(kāi)始角度、終止角度、是否是順時(shí)針?lè)较騽?chuàng)建圓弧(yes 順時(shí)針,no逆時(shí)針)
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
// 通過(guò)已有路徑創(chuàng)建路徑
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
PS:弧線參考系
2、對(duì)曲線的路徑設(shè)置及其他屬性的設(shè)置
//將UIBezierPath轉(zhuǎn)換成一個(gè)不可變的CGPathRef類(lèi)型數(shù)據(jù)類(lèi)似于UIColor的CGColor
@property(nonatomic) CGPathRef CGPath;
- (CGPathRef)CGPath;
#prama -------- 具體設(shè)計(jì)路徑的相關(guān)方法
// 路徑移動(dòng)到某一點(diǎn)(設(shè)置起點(diǎn)point即起點(diǎn)位置)
- (void)moveToPoint:(CGPoint)point;
// 在前往某一點(diǎn)(point)路徑上劃直線
- (void)addLineToPoint:(CGPoint)point;
//構(gòu)建三次貝塞爾曲線,參數(shù)終點(diǎn),兩個(gè)控制點(diǎn)
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
// 構(gòu)建二次貝塞爾曲線,終點(diǎn),一個(gè)控制點(diǎn)
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
// 實(shí)例方法創(chuàng)建圓弧
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise ;
// 閉合路徑,即在終點(diǎn)和起點(diǎn)連一根線
- (void)closePath;
// 移除路徑
- (void)removeAllPoints;
// 添加路徑
- (void)appendPath:(UIBezierPath *)bezierPath;
// 顛倒路徑,起點(diǎn)變終點(diǎn),終點(diǎn)變起點(diǎn)
- (UIBezierPath *)bezierPathByReversingPath;
// 對(duì)路徑進(jìn)行仿射變換
- (void)applyTransform:(CGAffineTransform)transform;
#prama ------- 路徑信息
@property(readonly,getter=isEmpty) BOOL empty; // 路徑是否為空
@property(nonatomic,readonly) CGRect bounds;// 它獲取曲線的外接矩形的frame
@property(nonatomic,readonly) CGPoint currentPoint;// path的當(dāng)前點(diǎn)位置。對(duì)于圓或者橢圓來(lái)說(shuō)是圓心,對(duì)于其他則是終點(diǎn)位置
- (BOOL)containsPoint:(CGPoint)point; // 路徑上是否包含某一點(diǎn)
#prama ------------- 繪圖屬性
@property(nonatomic) CGFloat lineWidth; // 線寬
/*
線帽風(fēng)格。線帽(line cap)是指線條兩端的外觀
typedef CF_ENUM(int32_t, CGLineCap) {
kCGLineCapButt, // 無(wú)端點(diǎn)
kCGLineCapRound, // 圓的
kCGLineCapSquare // 平直的(看起來(lái)和kCGLineCapButt沒(méi)啥差別···)
};
具體見(jiàn)圖“線帽風(fēng)格”
*/
@property(nonatomic) CGLineCap lineCapStyle;
/*
線段連接風(fēng)格
typedef CF_ENUM(int32_t, CGLineJoin) {
kCGLineJoinMiter, // 斜接
kCGLineJoinRound, // 圓潤(rùn)銜接
kCGLineJoinBevel // 有過(guò)渡斜面銜接
};
具體見(jiàn)圖“線段連接風(fēng)格”
*/
@property(nonatomic) CGLineJoin lineJoinStyle;
// 最大斜接長(zhǎng)度,只有在kCGLineJoinMiter下才有效果。因?yàn)楫?dāng)角度很小時(shí)斜接的長(zhǎng)度會(huì)非常的長(zhǎng)。所以可以設(shè)置一個(gè)最大斜接長(zhǎng)度。
// 當(dāng)斜接長(zhǎng)度超過(guò)了該設(shè)置則以kCGLineJoinBevel的樣式展示
@property(nonatomic) CGFloat miterLimit;
// 彎曲路徑的渲染精度,默認(rèn)為0.6,越小精度越高,相應(yīng)的更加消耗性能
@property(nonatomic) CGFloat flatness;
// 是否使用奇偶規(guī)則用于繪制路徑,默認(rèn)是NO(詳情見(jiàn)PS-1),如果使用-fill方法,則多邊形內(nèi)部填充顏色,外部不會(huì)填充
@property(nonatomic) BOOL usesEvenOddFillRule;
// 設(shè)置線型,pattern--C類(lèi)型的線型數(shù)據(jù)。如:CGFloat dashStyle[] = { 1.0f, 2.0f };count--pattern 包含元素的數(shù)量;phase--相位(一般是指,角度所在的象限)(一些舉例:PS-dash)
- (void)setLineDash:(nullable const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase;
- (void)getLineDash:(nullable CGFloat *)pattern count:(nullable NSInteger *)count phase:(nullable CGFloat *)phase;
#prama 一下設(shè)置路徑顏色分布兩種模式均需在設(shè)置顏色情況下:
#prama [[UIColor blueColor] set]、[[UIColor redColor] setFill]、[[UIColor yellowColor] setStroke];
// 填充模式
- (void)fill;
// 描邊模式
- (void)stroke;
// 設(shè)置填充/描邊的混合模式和透明度(CGBlendMode一般在設(shè)置圖片中用途較大,詳細(xì)見(jiàn)PS-2)
- (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void)strokeWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
// 添加剪裁
- (void)addClip;
**PS-1: 奇偶判斷規(guī)則(even-odd)--- 平面內(nèi)的任何一點(diǎn)P,引出一條 射線(這里強(qiáng)調(diào)一下,是射線而不是直線,一定要注意兩者的區(qū)別),注意不要經(jīng)過(guò)多邊形的頂點(diǎn),如果射線與多邊形的交點(diǎn)的個(gè)數(shù)為奇數(shù),則點(diǎn)P在多邊形的內(nèi)部,如果交點(diǎn)的個(gè)數(shù)為偶數(shù),則點(diǎn)P在多邊形的外部。
PS-1-擴(kuò)展: 非零纏繞規(guī)則(non-zero)---- 平面內(nèi)的任何一點(diǎn)P,引出一條 射線,注意不要經(jīng)過(guò)多邊形的頂點(diǎn)。然后將多邊形的邊矢量化,規(guī)定環(huán)繞數(shù)初始結(jié)果為0,多邊形的邊如果從射線的左邊穿過(guò)則加1,如果從射線的右邊穿過(guò)則減1,最終結(jié)果累加,如果為0,則點(diǎn)P在多邊的外面;如果非0則點(diǎn)P在多邊形的內(nèi)部。
**舉例: **
從上邊可以看到奇偶判斷規(guī)則與非零纏繞規(guī)則判斷的結(jié)果可能是矛盾的,所以遵循了什么什么規(guī)則,對(duì)于繪圖來(lái)說(shuō)很重要。
PS-dash:
PS-2: 混合模式:這一混合模式常用于圖片(image)設(shè)置,要特別注意kCGBlendModeOverlay(保持背景灰度)、kCGBlendModeColor(保持圖片灰度)、kCGBlendModeDestinationIn(保持圖片透明度信息)、kCGBlendModeDestinationOut(與圖片透明度相反信息)這四種混合模式
typedef CF_ENUM (int32_t, CGBlendMode) {
/* Available in Mac OS X 10.4 & later. */
kCGBlendModeNormal, // 保持原色
kCGBlendModeMultiply, // 混合了前景和背景的顏色樣本,最終顏色比原先的都暗
kCGBlendModeScreen, // 復(fù)合反轉(zhuǎn)的原圖和背景圖樣本,與Multiply相反
kCGBlendModeOverlay, // 可以保持背景色的明暗,也就是灰度信息.
kCGBlendModeDarken, // 從源圖像或北京選較暗的樣品,背景樣品被暗化樣品取代,也就是暗化。
kCGBlendModeLighten, // 與Darken相反(亮化)
kCGBlendModeColorDodge, //加亮背景反射源圖像(顏色變淺)
kCGBlendModeColorBurn,// 與Dodge相反(顏色變深)
kCGBlendModeSoftLight, // 根據(jù)源圖像樣本的顏色,變暗或減輕顏色,。如果源圖像樣本顏色比50%灰色輕,背景是減輕顏色比重,否則相反(柔光)
kCGBlendModeHardLight, // 與SoftLight相反(強(qiáng)光)
kCGBlendModeDifference, // 要么背景圖像樣本減去源圖像樣本顏色,或相反(差值),黑色則不變,白色則顛倒
kCGBlendModeExclusion, // 低對(duì)比度的Difference
kCGBlendModeHue, // 使用背景的亮度和飽和度值與源圖像的色調(diào)進(jìn)行混合(色調(diào))
kCGBlendModeSaturation, //使用背景的亮度值和色調(diào)與源圖像的飽和度值進(jìn)行混合(飽和度)
kCGBlendModeColor, //使用背景的亮度值與源圖像的飽和度值和色調(diào)進(jìn)行混合,這種模式保留圖像中灰色的水平。
kCGBlendModeLuminosity, //使用背景的飽和度值和色調(diào)與源圖像的亮度值進(jìn)行混合。這種模式創(chuàng)建的效果與Color創(chuàng)建的效果相反!
/*
R = D*Sa :結(jié)果色 = 目標(biāo)色*原色的透明度
R--結(jié)果色
S--原色
D--目標(biāo)色
Ra、Sa、Da分別為三種顏色的透明度
*/
kCGBlendModeClear, /* R = 0 */
kCGBlendModeCopy, /* R = S */
kCGBlendModeSourceIn, /* R = S*Da */
kCGBlendModeSourceOut, /* R = S*(1 - Da) */
kCGBlendModeSourceAtop, /* R = S*Da + D*(1 - Sa) */
kCGBlendModeDestinationOver, /* R = S*(1 - Da) + D */
kCGBlendModeDestinationIn, /* R = D*Sa */
kCGBlendModeDestinationOut, /* R = D*(1 - Sa) */
kCGBlendModeDestinationAtop, /* R = S*(1 - Da) + D*Sa */
kCGBlendModeXOR, /* R = S*(1 - Da) + D*(1 - Sa) */
kCGBlendModePlusDarker, /* R = MAX(0, (1 - D) + (1 - S)) */
kCGBlendModePlusLighter /* R = MIN(1, S + D) */
};
舉例代碼:
- (void)drawRect:(CGRect)rect {
[[UIColor blueColor] set];
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(120, 100)];
[bezierPath addLineToPoint:CGPointMake(220, 100)];
[bezierPath addLineToPoint:CGPointMake(270, 187)];
[bezierPath addLineToPoint:CGPointMake(220, 274)];
[bezierPath addLineToPoint:CGPointMake(120, 274)];
[bezierPath addLineToPoint:CGPointMake(70, 187)];
[bezierPath closePath];
bezierPath.lineWidth = 5.f;
bezierPath.lineJoinStyle = kCGLineJoinMiter;
bezierPath.usesEvenOddFillRule = NO;
[bezierPath fillWithBlendMode:kCGBlendModeNormal alpha:0.5];
[[UIColor redColor] setFill];
UIBezierPath * bezier_0 = [UIBezierPath bezierPath];
[bezier_0 moveToPoint:CGPointMake(170, 150)];
[bezier_0 addQuadCurveToPoint:CGPointMake(170, 274) controlPoint:CGPointMake(50, 50)];
bezier_0.lineWidth = 5.f;
[bezier_0 fillWithBlendMode:kCGBlendModeOverlay alpha:1.0];
[bezierPath appendPath:bezier_0];
[[UIColor purpleColor] setFill];
UIBezierPath * bezier_1 = [UIBezierPath bezierPath];
[bezier_1 moveToPoint:CGPointMake(170, 150)];
[bezier_1 addQuadCurveToPoint:CGPointMake(170, 274) controlPoint:CGPointMake(290, 50)];
bezier_1.lineWidth = 5.f;
[bezier_1 fillWithBlendMode:kCGBlendModeDarken alpha:1.0];
[bezierPath appendPath:bezier_1];
[[UIColor grayColor] setStroke];
UIBezierPath * bezier_3 = [UIBezierPath bezierPath];
[bezier_3 moveToPoint:CGPointMake(170, 150)];
[bezier_3 addCurveToPoint:CGPointMake(170, 274) controlPoint1:CGPointMake(50, 212) controlPoint2:CGPointMake(220, 180)];
bezier_3.lineWidth = 5.f;
[bezier_3 stroke];
[bezierPath appendPath:bezier_3];
[[UIColor greenColor] setStroke];
UIBezierPath * bezier_4 = [UIBezierPath bezierPath];
[bezier_4 moveToPoint:CGPointMake(170, 150)];
[bezier_4 addCurveToPoint:CGPointMake(170, 274) controlPoint1:CGPointMake(290, 212) controlPoint2:CGPointMake(120, 180)];
bezier_4.lineWidth = 5.f;
[bezier_4 stroke];
[bezierPath appendPath:bezier_4];
}