CAShapeLayer + BezierPath 处理图形编辑
2020-07-17 本文已影响0人
Realank
我们做图形编辑的时候,做了一个ShapeOperator类来操作图形,里面存储了图形的尺寸位置、旋转角度等信息,可以生成贝塞尔曲线,将其赋值给 CAShapeLayer即可完成渲染
旋转
SO上找到了操作贝塞尔曲线旋转的最佳实践:https://stackoverflow.com/questions/13738364/rotate-cgpath-without-changing-its-position
- 针对某个旋转,先以图形中心点的负值做移动,将中心点移动到“视图”左上角,再做旋转,然后再移动回来。
- 上面涉及三次 transform, 通过 transformConcat连接,计算出最终 transfrom,赋值给贝塞尔曲线的 transform 属性
平移
直接改变控制点即可
编辑控制点
以矩形为例,矩形有四个控制点,即几个角,如果没有旋转,拖动任意控制点就能改变图形的大小,也很直观,但是增加了旋转以后,复杂度陡然提升
矩形的表示
- 起始点 (x, y)
- 宽度 width
- 高度 height
- 旋转角度 rotation
控制点转换
- 如果没有旋转的图形,改变的控制点,很容易对应出起始点和宽高的改变,比如移动矩形左上角,就是把起始点变成拖动后的点,对应增加宽高
- 如果我们在一个旋转了一定角度的图形上,拖动控制点,我们需要知道这个控制点到底如何影响上面的尺寸位置的,拖动后的点的坐标,是未旋转的控制点位置,追加了旋转角度后的坐标
- 所以第一步是去除旋转的影响,iOS 为我们提供了 CGPoint 的 transform 变换的计算方法
CGPointApplyAffineTransform
- 上面旋转章节提到,做旋转需要三次 transform,把它 concat 后的最终 transfrom 做
CGAffineTransformInvert
,再应用给新的控制点,就得到了矩形未旋转时的新控制点位置,这就很容易把对应的起始点和宽高计算出来了,然后再把图形旋转到对应角度。 - 但是旋转以后,和以前的图形就不对齐了,这是个大坑,什么叫对齐?下面详细说
有旋转的控制点变换追加平移
- 我们插入了下面的图形,边长 a,图形中心点 (x, y)
- 然后旋转这个图形到下图,
- 接着拖动图形最下面的这个顶点,让图形变大成边长b,我们期望的是下图的效果
想达到上面的效果,我们有两种途径:
- 旋转中心点不变,仅改变边长尺寸,这样我们在做图形旋转的时候,先对图形进行(-x, -y)的移动,然后旋转,再进行 (x, y) 的移动,就能满足上图的效果(仅最底下的控制点变化了,其它边以及顶部控制点与原先重合)。但是这样有一个问题,下次旋转的时候,就不是沿着图形中心点旋转了,不过这个思路能帮我们找到正确答案
- 始终保持图形中心点旋转。但是,因为编辑后,边长从 a 变成 b ,图形中心点会变成 (x', y'),那旋转以后,图形的顶点和其它边就没法与原先重合,而是像下图
如果想达成期望的效果,在编辑图形的时候,还要对图形进行平移,这样旋转以后就能使顶部的点与原先重合
我们按途径1,以不变旋转中心点的方式旋转以后,图形的中心点变成了 (x", y"),可以从下图看到,(x", y") 与 (x', y') 存在平移,而此平移的变化由来,就是以 (x, y) 为中心,将(x', y')旋转图形所旋转的角度,变到了(x", y") ,移动量就是 (x" - x', y" - y')
这样,在编辑的时候,计算编辑前后的中心点变化,并应用上面的变换,得到图形需要平移的量,追加给图形,就可以达到预期的效果了
总结
图形编辑,有时候看起来很简单直观的事情,在实际代码上,就会非常困难,最近在做曲线的旋转,又是一个大坑,因为曲线的起始点,终点、控制点三个点,位置的不同,决定了曲线的形状,拖动终点或起点的时候,三个点都会变化,而且一会正一会负,需要根据象限分别判断曲线是凸的还是凹的,头大啊。。