[iOS Animation]-CALayer 3D变换

3D变换

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

在第三章中,我们提到了zPosition属性,可以用来让图层靠近或者远离相机(用户视角),transform属性(CATransform3D类型)可以真正做到这点,即让图层在3D空间内移动或者旋转。

和CGAffineTransform类似,CATransform3D也是一个矩阵,但是和2x3的矩阵不同,CATransform3D是一个可以在3维空间内做变换的4x4的矩阵(图5.6)。

[iOS Animation]-CALayer 3D变换_第1张图片

图5.6 对一个3D像素点做CATransform3D矩阵变换

和CGAffineTransform矩阵类似,Core Animation提供了一系列的方法用来创建和组合CATransform3D类型的矩阵,和Core Graphics的函数类似,但是3D的平移和旋转多处了一个z参数,并且旋转函数除了angle之外多出了x,y,z三个参数,分别决定了每个坐标轴方向上的旋转:

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z);
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz); 
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz);

 

你应该对X轴和Y轴比较熟悉了,分别以右和下为正方向(回忆第三章,这是iOS上的标准结构,在Mac OS,Y轴朝上为正方向),Z轴和这两个轴分别垂直,指向视角外为正方向(图5.7)。

[iOS Animation]-CALayer 3D变换_第2张图片

图5.7 X,Y,Z轴,以及围绕它们旋转的方向

由图所见,绕Z轴的旋转等同于之前二维空间的仿射旋转,但是绕X轴和Y轴的旋转就突破了屏幕的二维空间,并且在用户视角看来发生了倾斜。

举个例子:清单5.4的代码使用了CATransform3DMakeRotation对视图内的图层绕Y轴做了45度角的旋转,我们可以把视图向右倾斜,这样会看得更清晰。

结果见图5.8,但并不像我们期待的那样。

清单5.4 绕Y轴旋转图层

复制代码
@implementation ViewController - (void)viewDidLoad
{
    [super viewDidLoad]; //rotate the layer 45 degrees along the Y axis CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    self.layerView.layer.transform = transform;
} @end
复制代码

 

[iOS Animation]-CALayer 3D变换_第3张图片

图5.8 绕y轴旋转45度的视图

看起来图层并没有被旋转,而是仅仅在水平方向上的一个压缩,是哪里出了问题呢?

其实完全没错,视图看起来更窄实际上是因为我们在用一个斜向的视角看它,而不是透视

透视投影

在真实世界中,当物体远离我们的时候,由于视角的原因看起来会变小,理论上说远离我们的视图的边要比靠近视角的边跟短,但实际上并没有发生,而我们当前的视角是等距离的,也就是在3D变换中任然保持平行,和之前提到的仿射变换类似。

在等距投影中,远处的物体和近处的物体保持同样的缩放比例,这种投影也有它自己的用处(例如建筑绘图,颠倒,和伪3D视频),但当前我们并不需要。

为了做一些修正,我们需要引入投影变换(又称作z变换)来对除了旋转之外的变换矩阵做一些修改,Core Animation并没有给我们提供设置透视变换的函数,因此我们需要手动修改矩阵值,幸运的是,很简单:

CATransform3D的透视效果通过一个矩阵中一个很简单的元素来控制:m34。m34(图5.9)用于按比例缩放X和Y的值来计算到底要离视角多远。

[iOS Animation]-CALayer 3D变换_第4张图片

图5.9 CATransform3D的m34元素,用来做透视

m34的默认值是0,我们可以通过设置m34为-1.0 / d来应用透视效果,d代表了想象中视角相机和屏幕之间的距离,以像素为单位,那应该如何计算这个距离呢?实际上并不需要,大概估算一个就好了。

因为视角相机实际上并不存在,所以可以根据屏幕上的显示效果自由决定它的防止的位置。通常500-1000就已经很好了,但对于特定的图层有时候更小后者更大的值会看起来更舒服,减少距离的值会增强透视效果,所以一个非常微小的值会让它看起来更加失真,然而一个非常大的值会让它基本失去透视效果,对视图应用透视的代码见清单5.5,结果见图5.10。

清单5.5 对变换应用透视效果

复制代码
@implementation ViewController - (void)viewDidLoad
{
    [super viewDidLoad]; //create a new transform CATransform3D transform = CATransform3DIdentity; //apply perspective transform.m34 = - 1.0 / 500.0; //rotate by 45 degrees along the Y axis transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0); //apply to layer self.layerView.layer.transform = transform;
} @end
复制代码

 

[iOS Animation]-CALayer 3D变换_第5张图片

图5.10 应用透视效果之后再次对图层做旋转

你可能感兴趣的:([iOS Animation]-CALayer 3D变换)