iOS CoreAnimation专题——技巧篇(二)CAShapeLayer with Bezier Path - Layer世界的神奇画笔

前言

CALayer是CoreAnimation框架中的核心类,动画是基于绘图的,连图都绘不了还动个毛的画!而CALayer就是来解决绘图问题的。

CoreAnimation框架为我们实现了许多CALayer的子类,它们用来解决特定的问题,比如CATextLayer可以用来显示富文本,CAGradientLayer用来绘制颜色的线性渐变效果。既然它们都是CALayer的子类,它们就拥有CALayer所有的特点:可动画属性、隐式动画、transform变形等。

所有的CALayer子类

在CoreAnimation框架中的所有的CALayer的子类如下所示:

CAShapeLayer,用来根据路径绘制矢量图形

CATextLayer,绘制文字信息

CATransformLayer,使用单独的图层创建3D图形

CAGradientLayer,绘制线性渐变色

CAReplicatorLayer,高效地创建多个相似的图层并施加相似的效果或动画

CAScrollLayer,没有交互效果的滚动图层,没有滚动边界,可以任意滚动上面的图层内容

CATiledLayer,将大图裁剪成多个小图以提高内存和性能

CAEmitterLayer,各种炫酷的粒子效果

CAEAGLLayer,用来显示任意的OpenGL图形

AVPlayerLayer,用来播放视频

而我们在开发中使用频率最高的就是CAShapeLayer,我们将结合贝塞尔曲线详细讲解其使用。其他Specialized Layer请参阅这篇翻译的文章

CAShapeLayer

CAShapeLayer是一个通过矢量图形而不是bitmap(位图)来绘制的CALayer子类。你指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图形,最后CAShapeLayer就自动渲染出来了。当然,你也可以用Core Graphics直接向原始的CALyer的内容中绘制一个路径,相比直下,使用CAShapeLayer有以下一些优点:

渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。

高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形(backing image),所以无论有多大,都不会占用太多的内存。

不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉。

不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。

矢量图简介

在图形世界中有两种图形:位图(bitmap)和矢量图(vector)

位图是通过排列像素点来构造的,像素点的信息包括颜色+透明度(ARGB),颜色通过RGB来表示,所以一个像素一共有4个信息(透明度、R、G、B),每个信息的取值范围是0-255,也就是一共256个数,刚好可以用8位二进制来表示,所以每个像素点的信息通常通过32位(4字节)编码来表示,这种位图叫做32位位图,而一些位图没有Alpha通道,这样的位图每个像素点只有RGB信息,只需要24位就可以表示一个像素点的信息。

位图在进行变形(缩放、3D旋转等)时会重新绘制每个像素点的信息,所以会造成图形的模糊。

值得一提的是,对于GPU而言,它绘制位图的效率是相当高的,所以如果你要提高绘制效率,可以想办法把复杂的绘制内容转换成位图数据,然后丢给GPU进行渲染,比如使用CoreText来绘制文字。

关于位图,这里不做更详细的介绍。

矢量图

矢量图是通过对多个点进行布局然后按照一定规则进行连线后形成的图形。矢量图的信息总共只有两个:点属性和线属性。点属性包括点的坐标、连线顺序等;线属性包括线宽、描线颜色等。

每当矢量图进行变形的时候,只会把所有的点进行重新布局,然后重新按点属性和线属性进行连线。所以每次变形都不会影响线宽,也不会让图变得模糊。

如何重新布局是通过把所有点坐标转换成矩阵信息,然后通过矩阵乘法重新计算新的矩阵,再把矩阵转换回点信息。比如要对一个矢量图进行旋转,就先把这个矢量图所有的点转换成一个矩阵(x,y,0),然后乘以旋转矩阵:

(
cosa sina 0

-sina cosa 0

0 0 1)

得到新的矩阵(x·cosa-y·sina, x·sina+y·cosa, 0)
然后把这个矩阵转换成点坐标(x·cosa-y·sina, x·sina+y·cosa)这就是新的点了。对矢量图所有的点进行这样的操作后,然后重新连线,出现的新的图形就是旋转后的矢量图了。

关于矩阵计算和自定义矢量图的绘制,可以查看我的这个git项目:
DHVectorDiagram

构建CAShapeLayer

构建一个CAShapeLayer非常简单,对于所有CALayer的子类,它们的初始化都是一个简单的便利构造,像这样:

CAShapeLayer * shapeLayer = [CAShapeLayer layer];

像普通的CALayer一样,接下来你可以设置它的frame、背景颜色、寄宿图等,当然我们的CAShapeLayer肯定不是一个普通的layer,它是用来绘制矢量图的,通过传递给它的对象一个CGPathRef,CAShapeLayer就能以矢量图的形式将这个路径所表示的信息绘制出来。

在让CAShapeLayer渲染之前,我们可以先设置好线属性,比如我们设置线宽和描线颜色:

shapeLayer.lineWidth = 5;
shapeLayer.strokeColor = [UIColor redColor].CGColor;

stroke是描线的意思,我们后面还会接触到strokeStart和strokeEnd等更多的描线属性。

设置好了渲染信息后,我们可以构造一个路径来让CAShapeLayer帮我们绘制出来,这里我们先直接使用UIKit里面的贝塞尔曲线来构造一个简单的矩形路径:

UIBezierPath * path = [UIBezierPath    bezierPathWithRect:CGRectMake(0,0,40,40)];

这里需要注意的是,路径的坐标是相对于shapeLayer的左上角

然后把它的CGPath属性赋值给shapeLayer:

shapeLayer.path = path.CGPath;

最后把shapeLayer加到层级上来显示:

[self.view.layer addSublayer:shapeLayer];

运行一下会发现,我们的红色方框确实是画出来了,但是中间被填充成了黑色。这是因为CAShapeLayer的fillColor属性默认为黑色,fillColor表示的是填充颜色,将一个CAShapeLayer的路径的所有封闭区间填充成该颜色,如果你不想要填充的效果,你可以设置其为透明色:

shapeLayer.fillColor = [UIColor clearColor].CGColor;

贝塞尔曲线

贝塞尔曲线简介

贝塞尔曲线于1962年,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau算法开发,以稳定数值的方法求出贝塞尔曲线。

— 维基百科

线性贝塞尔曲线

给定点 P0P1 ,线性贝塞尔曲线只是一条两点之间的直线。这条线由下公式给出:

B(t)=P0+(P1P0)t=(1t)P0+tP1,t[0,1]

且其等同于线性插值

二阶贝塞尔曲线

二阶贝塞尔曲线的路径由给定点 P0P1P2 的函数B(t)追踪:

B(t)=(1t)2P0+2t(1t)P1+t2P2,t[0,1]

其中 P1 又叫做控制点
TrueType字型就运用了以贝塞尔样条组成的二阶贝塞尔曲线。

三阶贝塞尔曲线

P0P1P2P3 四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于 P0 走向

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值