阅读 50

学习CoreAnimation - 矩阵

矩阵:CGAffineTransformCATransform3D

CGAffineTransform

仿射变换2D,CG的前缀告诉我们,CGAffineTransform类型属于Core Graphics框架,Core Graphics实际上是一个严格意义上的2D绘图API,并且CGAffineTransform仅仅对2D变换有效。

采用3*3矩阵,并可用于2D条件下图层的旋转、缩放和平移。

单一变换

// 旋转:参数角度 M_PI表示180度,M_PI_4表示pi的四分之一
let tr = CGAffineTransform(rotationAngle: CGFloat(M_PI_4))
// 缩放:参数x或y缩放倍数
let tr = CGAffineTransform(scaleX: 0.1, y: 0.1)
// 平移:平移距离
let tr = CGAffineTransform(translationX: 0, y: 100)

// CGAffineTransform加入view中    
newView.layer.setAffineTransform(tr)
复制代码

组合变换

// 使用链式调用完成组合变换
// 而在组合变换中,之前的变换结果会影响之后的变换位置、大小。而不是从起始位置一次性的变换
let trs = CGAffineTransform(rotationAngle: CGFloat(M_PI_4))
            .scaledBy(x: 2, y: 1)
            .translatedBy(x: 100, y: 0)
            .inverted()
        
newView.layer.setAffineTransform(trs)
复制代码

CATransform3D

3D变换,用来调整图层和用户视角的远近。

在学习中我们知道这是一个4*4的矩阵,CATransform3D有m11到m44一共16个变量,结构如下:

public struct CATransform3D {

    public var m11: CGFloat
		....
    public var m44: CGFloat

    public init()

    public init(m11: CGFloat, m12: CGFloat, m13: CGFloat, m14: CGFloat, m21: CGFloat, m22: CGFloat, m23: CGFloat, m24: CGFloat, m31: CGFloat, m32: CGFloat, m33: CGFloat, m34: CGFloat, m41: CGFloat, m42: CGFloat, m43: CGFloat, m44: CGFloat)
}
复制代码

矩阵图如下:

5.7.jpeg 而每一个变量所代表的的值,都是3D变换中的一个动作,比如我们最常使用的平移、缩放和旋转,就是由这些4*4组合而来,在源码的注释中就能一探究竟

# 这是一个默认的结构
The identity transform: [1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1].

# 进行 x, y, z轴的平移操作
# m41 表示当前transform沿x轴方向平移,后面依次是m42沿y轴,m43沿z轴,m44是初始值1
Returns a transform that translates by '(tx, ty, tz)':
 * t' =  [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1].
 
# 进行x, y, z轴的缩放操作
# m11 表示沿x轴方向缩放,m22 表示沿y轴方向缩放,m33 表示沿z轴方向缩放
Returns a transform that scales by `(sx, sy, sz)':
 * t' = [sx 0 0 0; 0 sy 0 0; 0 0 sz 0; 0 0 0 1].

# 图层围绕向量三轴旋转angle对等的角度
# m12和m21表示z轴旋转,m23和m32表示x轴旋转,m13和m31表示y轴旋转
Rotate 't' by 'angle' radians about the vector '(x, y, z)' and return
 * the result. If the vector has zero length the behavior is undefined:
 * t' = rotation(angle, x, y, z) * t.

#m34表示透视效果,其他的不具有意义或为0

## 我们思考的同时观察矩阵图,前三行分别代表x,y,z轴,当我们做平移和缩放操作的时候,它是正向的,也就是和它本身代表的轴体系一致;当我们使用旋转操作的时候,它是逆向的,在矩阵图中m12和m21都不在z轴,m23和m32都不在y轴,这里它的计算方式很有意思。由于没有深挖,只能推到这一步,希望知道的小伙伴能告知一二
复制代码

无处不在旋转变换

5.6.jpeg

加入z变换(透视效果)

我们选择使用CATransform3D,主要是需要3D旋转的效果,不然我们完全可以考虑更简单的2D方案来实现

m34表示透视效果,值等于-1/d,d越小透视越明显,看到的旋转效果越明显,必须在有旋转的情况下设置。

var tr3D = CATransform3DIdentity
// 需要透视效果,要在设置m34后把't'加入进去
tr3D.m34 = -1/100
tr3D = CATransform3DRotate(tr3D, CGFloat(M_PI_4), 0, 0, 1)
// 不需要透视效果则不需要加't'
tr3D = CATransform3DMakeRotation(CGFloat(M_PI_4), 0, 1, 0)
newView.layer.transform = tr3D
复制代码

通常来说图层的背面是同时被绘制的,显示出来的内容是正面的镜像,当我们不需要用到背面内容的时候可以使用isDoubleSided,来避免资源的浪费,将不会绘制也不会展示任何内容,同时图层会保留在视图上

newView.layer.isDoubleSided = false
复制代码

转换

在CATransform3D中有用于二者转换的方法

func CATransform3DMakeAffineTransform(_ m: CGAffineTransform) -> CATransform3D
func CATransform3DGetAffineTransform(_ t: CATransform3D) -> CGAffineTransform
复制代码

扁平化机制

扁平化主要是在3D层层次的场景下(扩展内容CATransformLayer)

由于Core Animation图层存在于3D空间之内,但它们并不都存在同一个3D空间。每个图层的3D场景其实是被扁平化了,所以当我们正面观察一个图层,看到的实际上由子图层创建的想象出来的3D场景,但如果倾斜这个图层,会发现实际上这个3D场景仅仅是被绘制在图层的表面。

因此,在使用的时候,我们如果沿着屏幕轴心旋转,被看到的就是二维内容,如果沿着垂直屏幕轴心旋转,看到的就是三维的内容,而这个三维的内容就是依据角度绘制出来的一种维度的观感。

可以得出的结论是:

(1)当我们对已经转换的图层的内部图层做反方向转换,会抵消内部图层的转换效果。

(2)每个父视图会将它的子视图扁平化。

灭点

5.11.jpeg

灭点是以3D内容为中心的视图的中心点,当我们改变图层的位置中心点也会随之改变

如果想要保证图层都共享同一个灭点,需要分别设置每一个图层,并保证拥有相同的m34值,在图层变换前它们的灭点也是一致的。也可以使用sublayerTransform属性,通过一个父级图层的sublayerTransform来管理子的透视效果和灭点,并保持统一。

总结

矩阵可以对图层旋转,摆放或者扭曲的状态管理,以及可以将扁平物体转换成三维空间对象的CATransform3D。对我们研究三维空间的图层技术很有帮助,而不仅仅是在做添加圆角、阴影这些基础的操作。

文章分类
iOS
文章标签