UIBezierPath贝塞尔曲线

作者: 和珏猫 | 来源:发表于2016-07-27 22:01 被阅读1409次

    说起这个贝塞尔曲线,其实可以实现很多功能的:曲线图、折线图、进度条、画板、不规则图形等等。下边我们就来看看这条线。

    使用UIBezierPath类可以创建基于矢量的路径,这个类在UIKit中。此类是Core Graphics框架关于path的一个封装。使用此类可以定义简单的形状,如椭圆或者矩形,或者有多个直线和曲线段组成的形状。

    1.Bezier Path 基础

    UIBezierPath对象是CGPathRef数据类型的封装。path如果是基于矢量形状的,都用直线和曲线段去创建。我们使用直线段去创建矩形和多边形,使用曲线段去创建弧(arc),圆或者其他复杂的曲线形状。每一段都包括一个或者多个点,绘图命令定义如何去诠释这些点。每一个直线段或者曲线段的结束的地方是下一个的开始的地方。每一个连接的直线或者曲线段的集合成为subpath。一个UIBezierPath对象定义一个完整的路径包括一个或者多个subpaths。

    创建和使用一个path对象的过程是分开的。创建path是第一步,包含一下步骤:

    (1)创建一个Bezierpath对象。

    (2)使用方法moveToPoint:去设置初始线段的起点。

    (3)添加line或者curve去定义一个或者多个subpaths。

    (4)改变UIBezierPath对象跟绘图相关的属性。

    例如,我们可以设置stroked path的属性lineWidth和lineJoinStyle。也可以设置filled path的属性usesEvenOddFillRule。

    当创建path,我们应该管理path上面的点相对于原点(0,0),这样我们在随后就可以很容易的移动path了。为了绘制path对象,我们要用到stroke和fill方法。这些方法在current graphic context下渲染path的line和curve段。

    2、使用UIBezierPath创建多边形---在path下面添加直线条形成多边形

    多边形是一些简单的形状,这些形状是由一些直线线条组成,我们可以用moveToPoint: addLineToPoint:方法去构建。

    方法moveToPoint:设置我们想要创建形状的起点。从这点开始,我们可以用方法addLineToPoint:去创建一个形状的线段。

    我们可以连续的创建line,每一个line的起点都是先前的终点,终点就是指定的点。

    说明:closePath这个便利的方法使得我们不需要去画最后一条线

    1、不规则多边形。

    注意:这里的代码是在一个继承自UIView的- (void)drawRect:(CGRect)rect{}方法里面写的。

    如果我们讲最后一句[aPath stroke];换成[aPath fill];我们会发现图形被填充了:

    这个stroke就是“(用笔等)画”的意思,可以理解为边框,fill就是“填满”得意思

    2、画矩形。

    一般我们的矩形不会需要贝塞尔就可以自己画的,但是这个倒角矩形还是可以用的,看看这两个矩形有什么区别就知道了。

    我们点击进这个枚举类型可以看到

    左上角倒角、右上角倒角、左下角倒角、右下角倒角、全部倒角。这里我有两个问题:

    1、如果要求左上角和右上角同时倒角,而下边的两个角不倒角,怎么做?

    2、全部倒角,倒角是正方形边长的一半的时候就可以画圆圈或者圆饼了。

    因为我们可以看到上述是枚举值,,,哎,既然是枚举值,那么我们就能解决第一个问题了:

    这里我们可以讨论一下倒角成圆的话题:一般我们倒角成圆惯用的是下边的方法:

    xxx.layer.cornerRadius=10;

    xxx.layer.masksToBounds=YES;

    关于这种方法我们之前在《TableView 优化》的文章里面讲到过:如果页面上有很多个倒角,上述方法操作,会是GPU负载迅速上升,而解决方案是设置为:xxx.layer.shouldRasterize=YES;这样可以把负载转移给CPU,使得GPU和CPU比较平衡,进而使得tableview得到优化。(如果页面上只有一两个倒角的话,应该影响不大)

    而我们这里贝塞尔实现画圆的方法要比上述方法效率高,但是需要一些封装。

    3、画弧线。

    贝塞尔曲线,那么贝塞尔肯定能画弧线,画弧线需要注意的还挺多,我们先实现效果。如下图:

    首先我们看:有人将圆周率π写成了3.1415926,当然这样写是可以的,但是我们保持严谨的态度是将pi取xcode里面定义的,如下图宏定义pi=M_PI。

    然后我们点击进去看看xcode自带的pi的值,精确到了小数点后边35位。(这玩意越精确自然是越好的,当然不影响大局)

    然后我们再关注一下方法里面的这个参数:clockwise,有道词典翻译一下我们可以知道这个词是“顺时针方向”,方法里面是BOOL值,看上边的两个图片我们分别将BOOL值设置成了NO和YES,NO就是“不是顺时针方向”=逆时针方向,YES自然就是顺时针方向了。我们可以看到这两张图上边的弧线可以凑成一个圆。

    另外我们可以看到我们的135度是没有变化的,那么顺时针135度就真的是135度的弧线,但是逆时针135度就是360-135=225度。

    再就是我们看一下弧线的起点,为啥是在那里,这就要说起xy轴的问题了

    起点是在X轴的正方向上边。

    下边这个图告诉我们0的位置在X轴的正方向上边,同时这里也是一圈终止的地方,也就是2π,图中标明了是顺时针方向,所以上图我们的135°并且clockwise设置成YES的时候,是那个样子的。

    那么关于弧线的起点我们就是要自己去算了,就像下图是的,我的起点是在0度的基础上加了90度,终点是180度,顺时针防线,那么就是这样一个弧线。所以起点是自己可以计算的。

    还有一个有趣的现象:我选择fill,效果如下图。(是不是想起了小学的时候求这个的面积:扇形的面积-一个三角形的面积,哈哈)

    我以为选择fill后的效果是出来一个扇形呢,结果不是。

    4、画曲线

    贝塞尔曲线,既然叫这个名字,那么就应该对得起哈哈,自然可以画曲线的!

    首先绘制二次贝塞尔曲线。我是这么理解这个二次贝塞尔曲线的:只有一个弯儿

    我们知道两点确定一条直线,也可以这么说:两点之间直线最短。那么这个二次贝塞尔曲线我们就可以理解为是把一条直线朝着一个方向拉弯了,不管你往上还是往下拉,总之只朝着一个方向拉,那么这就是二次贝塞尔曲线。

    实际上按照这个贝塞尔曲线画二次贝塞尔曲线的方法:- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint中我们可以看到第一个参数是endPoint,也就是一开始我们有一个起点,这个endPoint是终点,这就是两个点。第二个参数是controlPoint,字面上的意思我们就能知道:控制点。这就是控制二次贝塞尔曲线拉弯程度的那个点。即下图所示。

    通过改变这个controlPoint点,我们可以得到不同形状的曲线。

    然后我们来看三次贝塞尔曲线。理解了二次的,三次的我们也就能理解了,还是先来一条直线,然后这次是两个方向拉这个直线,最好这两个方向是相反的,这样的话我们可以看得更清楚一些,当然我们也可以设置成相同方向的两个拉力,就是不明显而已。

    把stroke换成fill我们得到下边的图像:

    5、进度条

    下边红框的内容是重点:是贝塞尔曲线和CALayer产生联系的重要步骤,另外这个进度条的代码,可以优化一下,比方我们需要的是一个完成百分比85%的进度条,那么我们的self.shapeLayer.strokeEnd,那么我们加一个动画,在2秒内完成从0到85%的一个动画,这就是进度条。

    这里需要注意的是如果按照这个代码完成后,进度条只转了一圈就不转了,那么可以修改一下add的初始值,我的就是设置为0.1的时候,只转了一圈,其实也是在转,因为我在timer的事件中打了断点的,断了,不知道是什么原因,改成0.05以后,就一直不停的转了。。。

    暂且先研究了这么多,也不过是照着例子做了做,加了一些自己的理解,希望给大家带来帮助。

    具体如何折线图和曲线图这个暂且没有研究,如果有网友有相关好的demo,希望可以告诉我,在此先谢谢了。

    6、饼状图

    加入宏定义,这样就可以随意的设置圆的圆心和半径了。

    这是点击方法,就是不知道为什么点击两次。。。

    上边的点击事件修改为touchesBegan:点击就是一次了。

    在.h中将宏定义替换掉,这样就算外漏了,那么我们就可以在定义贝塞尔view的时候传入。

    当然,相对应的宏定义还要进行替换成self. xxx 。下图举例红色部分的修改。

    下图1、进行圆心和半径的传值     2、进行贝塞尔view的旋转      3、对方形的贝塞尔view进行裁剪

    动态效果图:点击区域进行打印:

    说明:这个圆饼只适合这个角度的,而且只能是三块饼,如果是四块饼或者五块饼,需要重新计算里面的self.radius*1.732/2、self.radius/2、self.radius+self.radius*1.732/2。还是太脆弱,不通用,后期会优化,期望效果是只要传入饼的块数等参数,直接就能出来想要的圆饼。后期优化吧。

    参考:

    UIBezierPath类

    介绍进度条

    贝塞尔曲线和帧动画结合

    贝塞尔曲线的原理

    最后一个最厉害,从数学的角度讲解了贝塞尔曲线的生成,最主要的是还有动画,可以更好的理解贝塞尔曲线的前因后果。

    最后,哪里不对的地方可以给我留言,我会及时改进的,谢谢大家。

    相关文章

      网友评论

        本文标题:UIBezierPath贝塞尔曲线

        本文链接:https://www.haomeiwen.com/subject/hemojttx.html