SDDrawView簡介
SDDrawView 是一款基于貝塞爾曲線的畫板組件,目前樣式包含線條疚膊、矩形、圓形、箭頭等樣式.具有調(diào)整畫板顏色,線條寬度,線條顏色等基本功能.后期準(zhǔn)備接入圖片涂改,橡皮擦功能,添加文字等功能.至于為什么要做SDDrawView這樣的一個三方畫板組件,其實SDDrawView大部分功能和現(xiàn)在網(wǎng)上的畫板組件都是類似的,是一個不折不扣的造輪子組件.其實,在網(wǎng)上找了很多的畫板組件三方中箭頭樣式令人不是太滿意顯得非常的生硬,大部分是一個矩形加一個三角形組成的多邊形箭頭.SDDrawView的箭頭樣式卻不同,SDDrawView箭頭樣式更類似于QQ截圖中的箭頭,更加的圓滑更加趨近于現(xiàn)實.接下來,看一下SDDrawView的效果演示圖.
SDDrawView快速集成
如何快速集成SDDrawView?非常簡單,只需要把SDDrawViewDemo下載下來,然后把Demo中的SDDrawView文件夾拖到你的工程中,然后如下導(dǎo)入頭文件即可.
#import "SDDrawView.h"
SDDrawView初始化也比較簡單.我們初始化一個SDDrawView對象然后添加到對應(yīng)的View視圖上即可.
//懶加載的形式初始化(可用可不用~)
- (SDDrawView *)drawView{
if(_drawView == nil){
_drawView = [[SDDrawView alloc] initWithFrame:[UIScreen mainScreen].bounds];
_drawView.drawViewColor = [UIColor whiteColor];//畫板顏色
_drawView.lineWidth = 2.0f;//線條寬度
_drawView.drawStyle = DrawStyleLine;//樣式
_drawView.lineColor = [UIColor redColor];//線條顏色
}
return _drawView;
}
圖形樣式選擇是一個枚舉值,只需設(shè)定對應(yīng)的樣式,就可繪制不同的圖形.
typedef enum : NSUInteger {
DrawStyleLine,
DrawStyleSquare,
DrawStyleCircle,
DrawStyleArrow
} DrawStyle;
這里說明一下SDDrawView所有的屬性和方法.
屬性或者方法 | 說明 |
---|---|
drawViewColor | 畫板顏色 |
lineWidth | 畫筆寬度 |
drawStyle | 繪制樣式 值為DrawStyle的枚舉值 |
lineColor | 畫筆顏色 |
- (void)cleanAction; | 清除畫板 |
- (void)rollbackAction; | 回退上一步 |
SDDrawView原理及箭頭繪制
SDDrawView的核心是使用貝塞爾曲線進(jìn)行的繪制.通過touchesBegan
渐扮、touchesMoved
嫂伞、touchesEnded
三個方法檢測用戶的觸摸點,然后調(diào)用[self setNeedsDisplay];
方法進(jìn)行重繪操作.
每一個圖形或者每一條線條都是一個SDBezierPath對象,一個SDBezierPath對象繼承于UIBezierPath,每一個SDBezierPath對象都具有以下的成員變量.
@property(nonatomic,strong) UIColor *lineColor;//繪制顏色
@property(nonatomic,assign) DrawStyle drawStyle;//繪制樣式
@property(nonatomic,assign) CGPoint startPoint;//當(dāng)是矩形或者是圓的時候,需要使用到這個屬性
@property(nonatomic,assign) CGPoint endPoint;//當(dāng)是矩形或者是圓的時候,需要使用到這個屬性
像線條、矩形聂抢、圓形這三種樣式還是非常容易實現(xiàn)的,那么如同下面箭頭形式的是如何實現(xiàn)的呢?
這里就使用到了極坐標(biāo)的知識,這里可以去看我寫的3D圖形:矩陣與線性變換里面有一部分寫到2D變換矩陣,如下圖所示.
為了讓各位看官更加直觀的了解旋轉(zhuǎn)矩陣公式,我們可以得知假定一個向量 \vec{u} 的坐標(biāo)為(x,y),那么經(jīng)過旋轉(zhuǎn)的 \theta 角度之后得到新的向量 \vec{u'},那么\vec{u'} 的坐標(biāo)(x',y'),x'和y'分別是多少呢?
假設(shè) 向量\vec{u}與x的夾角為 \phi ,那么 x = \vec{u}\cos{\phi} 和 y = \vec{u}\sin{\phi} (根據(jù)三角函數(shù)即可推導(dǎo)~)
那么我們可知\vec{u'}與x軸之間的夾角為 \phi + \theta,那么就有一下的推導(dǎo)過程
x' = \vec{u}\cos(\phi + \theta) = \vec{u}(\cos\phi\cos\theta - \sin\phi\sin\theta) = x\cos\theta - y\sin\theta
y' = \vec{u}\sin(\phi + \theta) = \vec{u}(\sin\phi\cos\theta + \cos\phi\sin\theta) = x\sin\theta + y\cos\theta
SDDrawView代碼中的體現(xiàn)如下所示.
- (CGPoint)rotateVecWithPx:(float)px py:(float)py ang:(double)ang newLen:(double)newLen{
double vx = px * cos(ang) - py * sin(ang);
double vy = px * sin(ang) + py * cos(ang);
double d = sqrt(vx * vx + vy * vy);
vx = vx / d * newLen;
vy = vy / d * newLen;
return CGPointMake((float) vx, (float) vy);
}
上面原理說的真的是好枯燥,那么久說一下實際過程中是如何處理的,我們通過觀看可以知道我們所需要的就是7個點的坐標(biāo)就可完成一個箭頭多邊形圖形的繪制.我們知道箭頭中軸線上的兩個坐標(biāo),也就是用戶開始觸摸和結(jié)束觸摸的兩個點.我們可以假定一下箭頭的長度比例和屬性.例如b和h到中軸線的角度都為30度,長度比例為0.2;c和e到中軸線的角度都為18度,長度比例為0.157;d和f到中軸線的角度都為90度(d和f是相對坐標(biāo)原點o').長度比例為0.023.如下所示.
//初始化箭頭的相關(guān)固定值
KEY_POINT_LEN1 = 70;
KEY_POINT_LEN2 = 55;
KEY_POINT_LEN3 = 8;
KEY_POINT_ANGLE1 = 30 * M_PI/ 180;
KEY_POINT_ANGLE2 = 18 * M_PI/ 180;
KEY_POINT_ANGLE3 = 90 * M_PI/ 180;
KEY_POINT_RATIO1 = 0.2;
KEY_POINT_RATIO2 = 0.157;
KEY_POINT_RATIO3 = 0.023;
坐標(biāo)系原點的建立說明: 當(dāng)需要知道bced四個點的時候,坐標(biāo)原點就在點a上;當(dāng)需要知道fd兩個點的時候,坐標(biāo)原點就在點o'上.
通過上面的一系列的準(zhǔn)備,接下來我們就可以計算各個點的坐標(biāo)情況了.代碼如下所示.
//配置箭頭的6個點位
double len1 = KEY_POINT_LEN1;
double len2 = KEY_POINT_LEN2;
double len3 = KEY_POINT_LEN3;
double len = sqrt(pow((endX - startX), 2) + pow((endY - startY), 2));
if (len * KEY_POINT_RATIO1 < KEY_POINT_LEN1) {
len1 = len * KEY_POINT_RATIO1;
}
if (len * KEY_POINT_RATIO2 < KEY_POINT_LEN2) {
len2 = len * KEY_POINT_RATIO2;
}
if (len * KEY_POINT_RATIO3 < KEY_POINT_LEN3) {
len3 = len * KEY_POINT_RATIO3;
}
CGPoint arrXY_11 = [self rotateVecWithPx:endX - startX py:endY - startY ang:KEY_POINT_ANGLE1 newLen:len1];
CGPoint arrXY_12 = [self rotateVecWithPx:endX - startX py:endY - startY ang:-KEY_POINT_ANGLE1 newLen:len1];
CGPoint arrXY_21 = [self rotateVecWithPx:endX - startX py:endY - startY ang:KEY_POINT_ANGLE2 newLen:len2];
CGPoint arrXY_22 = [self rotateVecWithPx:endX - startX py:endY - startY ang:-KEY_POINT_ANGLE2 newLen:len2];
CGPoint arrXY_31 = [self rotateVecWithPx:startX - endX py:startY - endY ang:KEY_POINT_ANGLE3 newLen:len3];
CGPoint arrXY_32 = [self rotateVecWithPx:startX - endX py:startY - endY ang:-KEY_POINT_ANGLE3 newLen:len3];;
//轉(zhuǎn)換到原始坐標(biāo)系中
float x11 = endX - arrXY_11.x;
float y11 = endY - arrXY_11.y;
float x12 = endX - arrXY_12.x;
float y12 = endY - arrXY_12.y;
float x21 = endX - arrXY_21.x;
float y21 = endY - arrXY_21.y;
float x22 = endX - arrXY_22.x;
float y22 = endY - arrXY_22.y;
float x31 = startX - arrXY_31.x;
float y31 = startY - arrXY_31.y;
float x32 = startX - arrXY_32.x;
float y32 = startY - arrXY_32.y;
然后我們只需要把我們的這7個點添加到UIBezierPath對象即可.
[self moveToPoint:endPoint];
[self addLineToPoint:CGPointMake(x11, y11)];
[self addLineToPoint:CGPointMake(x21, y21)];
[self addLineToPoint:CGPointMake(x32, y32)];
[self addLineToPoint:CGPointMake(x31, y31)];
[self addLineToPoint:CGPointMake(x22, y22)];
[self addLineToPoint:CGPointMake(x12, y12)];
結(jié)束
SDDrawView的分享就到這里了,如果需要歡迎下載收藏點贊. 如果有任何問題歡迎指導(dǎo)批評,我會及時回復(fù)的.最后添加上github的傳送門.感謝查看本篇博客.
→SDDrawView傳送門