您的位置:首页 > 移动开发 > IOS开发

iOS最全面的关于CATransform3D的解析

2017-02-21 10:27 351 查看
CATransform3D
QuartzCore
下声明的一个结构体,文档对它的描述:

The transform matrix is used to rotate, scale, translate, skew, and project the layer content. Functions are provided for creating, concatenating, and modifying CATransform3D data.

它是用来对一个layer的内容进行旋转、缩放、平移、扭转变化的变形矩阵,它提供了一些创建、叠加和修改(layer的)CATransform3D数据的函数。

它作为
CALayer
的一个属性对外访问,注释说明:

A transform applied to the layer relative to the anchor point of its bounds rect. Defaults to the identity transform. Animatable.

它以一个layer的锚点为准,将形变作用于此layer的范围内。默认是初始值,
Animatable
意味着此属性可以作用于动画。


分析

首先来看它的定义:
public struct CATransform3D {

public var m11: CGFloat

public var m12: CGFloat

public var m13: CGFloat

public var m14: CGFloat

public var m21: CGFloat

public var m22: CGFloat

public var m23: CGFloat

public var m24: CGFloat

public var m31: CGFloat

public var m32: CGFloat

public var m33: CGFloat

public var m34: CGFloat

public var m41: CGFloat

public var m42: CGFloat

public var m43: 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)
}


属性


它有16个存储属性,含义如下:

m11:x轴方向进行缩放

m12:和m21一起决定z轴的旋转

m13:和m31一起决定y轴的旋转

m14:

m21:和m12一起决定z轴的旋转

m22:y轴方向进行缩放

m23:和m32一起决定x轴的旋转

m24:

m31:和m13一起决定y轴的旋转

m32:和m23一起决定x轴的旋转

m33:z轴方向进行缩放

m34:透视效果,m34 = -1 / D,D越小,透视效果越明显,必须在有旋转效果的前提下,才会看到透视效果。

m41:x轴方向进行平移

m42:y轴方向进行平移

m43:z轴方向进行平移

m44:初始为1



矩阵乘法计算

同时它声明了一个
默认构造器
和一个
成员逐一构造器
,但这并不常用。

函数

/* 初始化一个CATransform3D的实例,默认的值是[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]*/
public let CATransform3DIdentity: CATransform3D

/* 判断一个CATransform3D的实例是否是初始化值。*/
public func CATransform3DIsIdentity(_ t: CATransform3D) -> Bool

/* 判断两个CATransform3D的实例的值是否相等。*/
public func CATransform3DEqualToTransform(_ a: CATransform3D, _ b: CATransform3D) -> Bool

/* 以默认值为基准,返回一个平移'(tx, ty, tz)'后的CATransform3D实例t':
* t' =  [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1]
* tx, ty, tz分别代表在x方向、y方向、z方向的位移量 */
public func CATransform3DMakeTranslation(_ tx: CGFloat, _ ty: CGFloat, _ tz: CGFloat) -> CATransform3D

/* 以默认值为基准,返回一个缩放'(sx, sy, sz)'后的CATransform3D实例t':
* t' = [sx 0 0 0; 0 sy 0 0; 0 0 sz 0; 0 0 0 1]
* sx, sy, sz分别代表在x方向、y方向、z方向的缩放比例,缩放是以layer的中心对称变化
* 当sx < 0时,layer会在缩放的基础上沿穿过其中心的竖直线翻转
* 当sy < 0时,layer会在缩放的基础上沿穿过其中心的水平线翻转 */
public func CATransform3DMakeScale(_ sx: CGFloat, _ sy: CGFloat, _ sz: CGFloat) -> CATransform3D

/* 以默认值为基准,返回一个沿矢量'(x, y, z)'轴线,逆时针旋转'angle'弧度后的CATransform3D实例
* 弧度 = π / 180 × 角度,'M_PI'代表180角度
* x,y,z决定了旋转围绕的轴线,取值为[-1, 1]。例如(1,0,0)是绕x轴旋转,(0.5,0.5,0)是绕x轴与y轴夹角45°为轴线旋转 */
public func CATransform3DMakeRotation(_ angle: CGFloat, _ x: CGFloat, _ y: CGFloat, _ z: CGFloat) -> CATransform3D

/* 以't'为基准,返回一个平移'(tx, ty, tz)'后的CATransform3D实例t':
* t' = translate(tx, ty, tz) * t.
* '(tx, ty, tz)'同'CATransform3DMakeTranslation' */
public func CATransform3DTranslate(_ t: CATransform3D, _ tx: CGFloat, _ ty: CGFloat, _ tz: CGFloat) -> CATransform3D

/* 以't'为基准,返回一个缩放'(sx, sy, sz)'后的CATransform3D实例t':
* t' = scale(sx, sy, sz) * t.
* '(sx, sy, sz)'同'CATransform3DMakeScale' */
public func CATransform3DScale(_ t: CATransform3D, _ sx: CGFloat, _ sy: CGFloat, _ sz: CGFloat) -> CATransform3D

/* 以't'为基准,返回一个沿矢量'(x, y, z)'轴线,逆时针旋转'angle'弧度后的CATransform3D实例t':
* t' = rotation(angle, x, y, z) * t.
* '(angle, x, y, z)'同'CATransform3DMakeRotation' */
public func CATransform3DRotate(_ t: CATransform3D, _ angle: CGFloat, _ x: CGFloat, _ y: CGFloat, _ z: CGFloat) -> CATransform3D

/* 叠加两个CATransform3D实例的值并返回得到的CATransform3D实例t':
* t' = a * b. */
public func CATransform3DConcat(_ a: CATransform3D, _ b: CATransform3D) -> CATransform3D

/* 反转一个CATransform3D实例并返回结果
* 如果没有翻转则返回原始矩阵 */
public func CATransform3DInvert(_ t: CATransform3D) -> CATransform3D

/* 将一个CGAffineTransform实例转换得到一个同样效果的CATransform3D实例 */
public func CATransform3DMakeAffineTransform(_ m: CGAffineTransform) -> CATransform3D

/* 判断一个CATransform3D实例能否被成功的转换成一个CGAffineTransform实例 */
public func CATransform3DIsAffine(_ t: CATransform3D) -> Bool

/* 将一个CATransform3D实例转换得到一个同样效果的CGAffineTransform实例
* 如果不能成功转换,则返回空值 */
public func CATransform3DGetAffineTransform(_ t: CATransform3D) -> CGAffineTransform


基本使用


平移

let transformTranslation = CATransform3DMakeTranslation(0, 100, 0)
imageView.layer.transform = transformTranslation


缩放

let transformSclae = CATransform3DMakeScale(0.5, 0.5, 1)
imageView.layer.transform = transformSclae



旋转


let transformRotation = CATransform3DMakeRotation(CGFloat(M_PI_4), 0, 0, 1)
imageView.layer.transform = transformRotation



叠加


let transformTranslation = CATransform3DMakeTranslation(0, 100, 0)
let transformSclae = CATransform3DMakeScale(0.5, 0.5, 1)
let transformConcat = CATransform3DConcat(transformTranslation, transformSclae)

imageView.layer.transform = transformConcat


反转


let transformSclae = CATransform3DMakeScale(0.5, 0.5, 1)
let transformInvert = CATransform3DInvert(transformSclae)

imageView.layer.transform = transformInvert


注意问题

CATransform3DConcat
有什么用?如果想同时平移和缩放为什么不先使用
CATransform3DMakeTranslation
再使用
CATransform3DMakeScale
来实现?

这样的使用结果只有最后的修改或赋值才会生效。

回头看'CATransform3DMake...'系列函数的说明:以默认值为基准,...先使用
CATransform3DMakeTranslation
平移后,值是
[1
0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1]
。接下来使用
CATransform3DMakeScale
时,修改的值以初始化的默认值
[1
0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]
为基准,得到的值为
[sx 0 0
0; 0 sy 0 0; 0 0 sz 0; 0 0 0 1]
,上一步平移的修改不再生效,以此类推。

你还可以使用不带'Make'的'CATransform3D...'系列函数,它需要传入一个
CATransform3D
实例,并以此为基准进行形变,可以实现和
CATransform3DConcat
同样的效果。

为什么使用
CATransform3DMakeRotation
CATransform3DRotate
,绕平行于x轴或y轴的某一条线旋转后,感觉内容被压扁了,并没有旋转呢?

其实是旋转了,只不过没有透视效果(简单来说就是近大远小的视觉效果),看起来就像是”压扁了“。

如果想要内容看起来有旋转的立体效果,可以修改实例
m34
属性的值,它决定透视效果,
m34
= -1 / D
D
越小,透视效果越明显。你可以这样使用:
var transform = CATransform3DIdentity
transform.m34 = -1 / 400.0
transform = CATransform3DRotate(transform, CGFloat(M_PI_4), 0, 1, 0)
imageView.layer.transform = transform


D
具体的取值范围需要开发者根据实际显示效果调试,建议取400左右。

另外,将上面代码旋转的角度变成
M_PI
(180°)后,可以观察到它的背面。图层是双面绘制的,背面显示的是正面的一个镜像图片。

当场景中不需要展示背面内容时,这个机制有可能让GPU绘制了无意义的内容,造成资源浪费。我们可以通过
CALayer
isDoubleSided
属性来控制图层的背面是否要被绘制。这是一个
Bool
类型,默认为
true
,如果设置为
false
,将会取消背面的绘制工作。

使用
CATransform3DMakeTranslation
CATransform3DTranslate
改变z方向位移量?

这样会改变不同视图之间的遮挡关系。

3D空间中,视图在z方向也有值,默认值是0,你可以想象成里手机屏幕的远近。当z值越大,表示离屏幕越近。但我建议不要这样使用。

比如我添加了两张图片,遮挡关系是这样的:

然后使用
CATransform3DMakeTranslation
修改了下图的z方向位移量为1,变成了这样:

看起来下图盖住了上图,但是来看图层层级:

上图依然处于下图的”上部“,这样的操作有可能造成布局紊乱或显示异常,请慎用。

修改
CATransform3D
frame
的影响?

修改
CATransform3D
的值也同样会作用于
frame


这意味着,使用
CATransform3DMakeRotation
CATransform3DRotate
后,view的
frame
有可能发生变化。例如将一个正方逆时针旋转45°后,此时的
width
就变成了其对角线的长度,同样
x
y
的值也发生了改变。

若在动画开启后,默认情况下视图的
frame
会直接更新到最新的状态,也就是动画将要运动到的位置。因此,在动画期间处理view的点击事件需要特别考虑。

CATransform3D
CGAffineTransform
的区别?

CATransform3D
包含于
QuartzCore
,是贯穿
Core
Animation
的一个4x4标准变形矩阵,作为
CALayer
的一个属性,常用来对内容进行3D变换。由于 
Core
Animation
 是建立在
OpenGL
之上的,
CALayer
OpenGL
结构的一种封装,所以
CATransform3D
有着与
OpenGL
模型视图矩阵相同的内部结构。
CGAffineTransform
包含于
Core
Graphics
,是一个3x3的仿射矩阵,作为
UIView
的一个属性,常用来绘制2D图像或进行2D变换。

当满足条件时,
CATransform3D
CGAffineTransform
可以通过各自提供的函数进行等价转换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios ui 动画