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传送门



你可能感兴趣的:(SDDrawView:画板组件,这里的箭头很有特点!)