SDDrawView:画板组件,这里的箭头很有特点!


SDDrawView简介


SDDrawView 是一款基于贝塞尔曲线的画板组件,目前样式包含线条、矩形、圆形、箭头等样式.具有调整画板颜色,线条宽度,线条颜色等基本功能.后期准备接入图片涂改,橡皮擦功能,添加文字等功能.至于为什么要做SDDrawView这样的一个三方画板组件,其实SDDrawView大部分功能和现在网上的画板组件都是类似的,是一个不折不扣的造轮子组件.其实,在网上找了很多的画板组件三方中箭头样式令人不是太满意显得非常的生硬,大部分是一个矩形加一个三角形组成的多边形箭头.SDDrawView的箭头样式却不同,SDDrawView箭头样式更类似于QQ截图中的箭头,更加的圆滑更加趋近于现实.接下来,看一下SDDrawView的效果演示图.


SDDrawView快速集成


如何快速集成SDDrawView?非常简单,只需要把SDDrawViewDemo下载下来,然后把Demo中的SDDrawView文件夹拖到你的工程中,然后如下导入头文件即可.

#import "SDDrawView.h"

SDDrawView初始化也比较简单.我们初始化一个SDDrawView对象然后添加到对应的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;
    
}

图形样式选择是一个枚举值,只需设定对应的样式,就可绘制不同的图形.

typedef enum : NSUInteger {
    DrawStyleLine,
    DrawStyleSquare,
    DrawStyleCircle,
    DrawStyleArrow
} DrawStyle;

这里说明一下SDDrawView所有的属性和方法.

属性或者方法 说明
drawViewColor 画板颜色
lineWidth 画笔宽度
drawStyle 绘制样式 值为DrawStyle的枚举值
lineColor 画笔颜色
- (void)cleanAction; 清除画板
- (void)rollbackAction; 回退上一步


SDDrawView原理及箭头绘制


SDDrawView的核心是使用贝塞尔曲线进行的绘制.通过touchesBegantouchesMovedtouchesEnded 三个方法检测用户的触摸点,然后调用[self setNeedsDisplay];方法进行重绘操作.

每一个图形或者每一条线条都是一个SDBezierPath对象,一个SDBezierPath对象继承于UIBezierPath,每一个SDBezierPath对象都具有以下的成员变量.

@property(nonatomic,strong) UIColor *lineColor;//绘制颜色
@property(nonatomic,assign) DrawStyle drawStyle;//绘制样式

@property(nonatomic,assign) CGPoint startPoint;//当是矩形或者是圆的时候,需要使用到这个属性
@property(nonatomic,assign) CGPoint endPoint;//当是矩形或者是圆的时候,需要使用到这个属性

像线条、矩形、圆形这三种样式还是非常容易实现的,那么如同下面箭头形式的是如何实现的呢?

绘制分析图

这里就使用到了极坐标的知识,这里可以去看我写的3D图形:矩阵与线性变换里面有一部分写到2D变换矩阵,如下图所示.

为了让各位看官更加直观的了解旋转矩阵公式,我们可以得知假定一个向量 \vec{u} 的坐标为(x,y),那么经过旋转的 \theta 角度之后得到新的向量 \vec{u'},那么\vec{u'} 的坐标(x',y'),x'和y'分别是多少呢?

假设 向量\vec{u}与x的夹角为 \phi ,那么 x = \vec{u}\cos{\phi}y = \vec{u}\sin{\phi} (根据三角函数即可推导~)

那么我们可知\vec{u'}与x轴之间的夹角为 \phi + \theta,那么就有一下的推导过程
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代码中的体现如下所示.


- (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个点的坐标就可完成一个箭头多边形图形的绘制.我们知道箭头中轴线上的两个坐标,也就是用户开始触摸和结束触摸的两个点.我们可以假定一下箭头的长度比例和属性.例如b和h到中轴线的角度都为30度,长度比例为0.2;c和e到中轴线的角度都为18度,长度比例为0.157;d和f到中轴线的角度都为90度(d和f是相对坐标原点o').长度比例为0.023.如下所示.

        //初始化箭头的相关固定值
        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;

坐标系原点的建立说明: 当需要知道bced四个点的时候,坐标原点就在点a上;当需要知道fd两个点的时候,坐标原点就在点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];;
        
        //转换到原始坐标系中
        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)];


结束


SDDrawView的分享就到这里了,如果需要欢迎下载收藏点赞. 如果有任何问题欢迎指导批评,我会及时回复的.最后添加上github的传送门.感谢查看本篇博客.

→SDDrawView传送门



©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容