将来的自己,会感谢现在努力的自己!

0%

CATransform3D详解

一、view图层

既然要了解就必须先要了解view和layer之间的关系。

1.1、view与layer职责

在ios中,每个view都至少会对应一个layer,layer主要负责绘制图层,显示效果。而view则是可以响应用户的触摸事件。属于各有各的分工。

1.2、坐标系

iOS中坐标系是一个三维坐标系,视角垂直于屏幕,x轴向右,y轴向下,z轴垂直屏幕向外

1.3、坐标系原点

1.3.1、默认情况

默认情况下,view相对于子视图的坐标原点为(0,0)【子视图是以父试图原点进行参照布局的】。这种情况一般是设置view的frame以及bounds为(0,0,200,100),注意设置bounds时x和y必须均为0.

1.3.2、非默认情况

非默认情况下,假如bounds为(20,20,200,100),这个时候,并不是将当前图层的坐标原点相对于图层左上角偏移了(20,20),而是将图层左上角点的坐标设置成了(20,20),本地坐标系的坐标原点偏移了(-20,-20)。如果view的子视图的frame.x = 0,frame.y = 0,此时这个子视图的左上角就会出现在view的左上角(-20,-20)处。

1.3.3、改变layer的anchorPoint

改变layer的anchorPoint,layer的anchorPoint默认值是(0.5,0.5)。layer的anchorPoint的具体作用在下面讨论。

二、positon与anchorPoint

view的frame,bounds,center对应layer中的frame,bounds,position。layer中的anchorPoint在view中是没有的。以下说的都是layer的属性,除了anchorPoint以外,其他的都跟view意义相同。

2.1、不得不说一下iOS确定一个图层位置的原理

在设置一个图层frame等跟位置有关的属性时,
layer.position.x = layer.frame.origin.x + layer.anchorPoint.x * layer.frame.size.width
layer.position.y = layer.frame.origin.y + layer.anchorPoint.y * layer.frame.size.height
等同于该图层layer的position。(其实叫center或者position为中心点不太准确,应该叫做基准点较为准确一点。至于为什么,继续看吧)

2.2、在layer的frame确定的情况下,更改anchorPoint

2.2.1、anchorPoint是什么?

anchorPoint默认值是(0.5,0.5),对应于layer的中心点。(0,0)对应左上角,(1,1)对应于右下角,范围在0 - 1之间。在做旋转动画时,anchorPoint指示着旋转的基准点,即是围绕着该点旋转。

2.2.2、改变anchorPoint,影响什么

假如layer的frame都确定了,且width和height都不为0.此时改变anchorPoint由默认值(0.5,0.5)变为(1.0,1.0),改变后view的位置和大小会发生变化么?
大小不会改变,位置会发生变化。此时,view的center坐标点不会发生变化,假如为(200,300)。但是layer的位置会发生变化。因为我们将anchorPoint改为了(1.0,1.0)为右下角的点。所以此时view右下角的点会在view之前的center点位置上。即此时view的右下角会在点(200,300)上。
此时:
layer.frame.origin.x = layer.position.x - layer.anchorPoint.x * layer.frame.size.width
layer.frame.origin.y = layer.position.y - layer.anchorPoint.y * layer.frame.size.height

2.2.3、结论

单独改变anchorPoint,会影响layer的frame.origin.x和layer.frame.origin.y,以及layer做动画时的基准点。
不会影响position的值,但是基准点变了。所以同样适用于view。
要想不让layer位置改变,只能重新计算新的center

1
2
3
4
5
6
- (void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGRect oldFrame = view.frame;
view.layer.anchorPoint = anchorPoint;
view.center = CGPointMake(oldFrame.origin.x + oldFrame.size.width * anchorPoint.x,oldFrame.origin.y + oldFrame.size.height * anchorPoint.y);
}

三、CATransform3D(3D变换)

3.1、认识CATransform3D

CATransform3D 的数据结构定义了一个同质的三维变换(4x4CGFloat值的矩阵),用于图层的旋转,缩放,偏移,歪斜和应用的透视。CATransform3D的结构体定义及各成员变量的职能如下:

1
2
3
4
5
6
7
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
};

3.2、各个api对应参数:

1
2
3
CATransform3DRotate    (CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z)//旋转
CATransform3DScale (CATransform3D t, CGFloat sx, CGFloat sy, CGFloat sz)//缩放
CATransform3DTranslate (CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz)//平移

3.3、改变其中的值,对矩阵的影响

3.3.1、改变缩放与平移时

1
2
3
4
5
6
7
struct CATransform3D
{
CGFloat m11 = sx, m12 = 0, m13 = 0, m14 = 0;
CGFloat m21 = 0, m22 = sy, m23 = 0, m24 = 0;
CGFloat m31 = 0, m32 = 0, m33 = sz, m34 = 0;
CGFloat m41 = tx, m42 = ty, m43 = tz, m44 = 1;
};

3.3.2、改变旋转参数

注意:这里的x,y,z只有1或者-1两个值,也就是如果x如果大于0即为1,小于0即为-1,否则为0。
sin与cos都是针对弧度进行计算。

1
2
3
4
5
6
7
8
9
10
11
12
float u = x/sqrt(x*x + y*y + z*z)//sqrt为开方
falot v = y/sqrt(x*x + y*y + z*z)
float w = z/sqrt(x*x + y*y + z*z)
float θ = angle

struct CATransform3D
{
CGFloat m11 = u²+(1-u²)*cosθ, m12 = uv*(1-cosθ)-w*sinθ, m13 = uw*(1-cosθ)+v*sinθ, m14 = 0;
CGFloat m21 = uv*(1-cosθ)+w*sinθ, m22 = v²+(1-v²)*cosθ, m23 = vw*(1-cosθ)-u*sinθ, m24 = 0;
CGFloat m31 = uw*(1-cosθ)-v*sinθ, m32 = vw*(1-cosθ)+u*sinθ, m33 = w²+(1-w²)*cosθ, m34 = 0;
CGFloat m41 = 0, m42 = 0, m43 = 0, m44 = 1;
};

CATransform3DRotate(CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
在经过以上函数生成矩阵之后,用生成的矩阵 x 传进来的矩阵t,计算出新的矩阵。

1
2
3
4
5
6
7
struct CATransform3D
{
CGFloat m11(x缩放), m12(切变) , m13(切变) , m14();
CGFloat m21(切变) , m22(y缩放), m23(切变) , m24();
CGFloat m31(切变) , m32(切变) , m33(z缩放), m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义);
CGFloat m41(x平移), m42(y平移), m43(z平移), m44();
};

参考:
图形变换之基本矩阵变换