最近项目中用到CATransform3D实现动画效果,感觉有点力不从心,对CALayer的一些属性模棱两可,故做了一番整理。
首先要知道的几个概念:
1、三维坐标系:视角垂直与屏幕而言,x轴向右,y轴向下,z轴垂直屏幕向外。
2、坐标系原点:ios默认
以图层的左上角点为坐标原点,osx默认以图层左下角为坐标原点。注意是默认,因为图层的坐标原点是可以设置的,下面会介绍。
3、view于layer的关系:对于UIView对象,layer属性承担了显示的职能,对view设置frame和bounds就是对view的layer设置,因此下面将不区分view和layer。
4、图层的锚点anchorPoint:是一个CGPoint值,x,y取值范围(0~1),默认为(0.5,0.5) 对于图层本身而言,顾名思义,锚点就用来定位图层的点。锚点有两个职能:(1)与position一同确定图层相对于父图层的位置;(2)作为图层旋转、平移、缩放的中心。
5、决定图层位置的position:图层的锚点相对于父图层坐标系原点的偏移。
6、frame和bounds:这是不太容易拎清的两个属性。frame决定图层在父图层坐标系中的位置和大小,设置frame会改变图层的position和bounds,例如设置frame为CGRectMake(20, 20, 200, 100),不另外设置anchorPoint(默认为(0.5,0.5))position以及bounds,那么这个图层的position为(20+200/2.0,20+100/2.0),bounds为(0,0,200,100)。因此frame的x和y并不是position的x和y,这点需要注意;bounds决定图层在本地坐标系中的位置和大小,即决定图层的坐标原点和大小。因此对一个图层设置bounds会影响其本身的大小,但不会影响其本身在父图层中的位置,影响是子图层的位置,因为子图层的位置是以其坐标原点为参照的。需要注意的是,假如对一个图层设置了bounds为CGRectMake(20, 20, 200, 100),并不是将当前图层的坐标原点相对于图层左上角偏移了(20,20),而是将图层左上角点的坐标设置成了(20,20),本地坐标系的坐标原点偏移了(-20,-20)。因此,要想使本地坐标系的坐标原点偏移(20,20),bounds的x,y应当设置为-20。
self.superLayer = [CALayer layer];
self.superLayer.frame = CGRectMake(0, 0, 200, 100);
self.superLayer.bounds = CGRectMake(-20, -20, 200, 100);
self.superLayer.backgroundColor = [UIColor orangeColor].CGColor;
[self.view.layer addSublayer:self.superLayer];
self.subLayer = [CALayer layer];
self.subLayer.frame = CGRectMake(0, 0, 100, 50);
self.subLayer.backgroundColor = [UIColor greenColor].CGColor;
[self.superLayer addSublayer:self.subLayer];
7、图层的这些属性:frame、bounds、position、anchorPoint,该怎么使用呢?前面介绍过frame会改变图层的position和bounds,因此通常有两种用法:(1)设置frame和bounds(通常只设置frame) (2)设置bounds、position、anchorPoint。什么时候用第(1)种?大多数人都习惯用frame吧。是的,只要在图层的变化(transform)过程中是以图层中心为锚点的,直接用第(1)种用法,即设置frame就好了,因为anchorPoint值为(0.5,0.5)默认为图层中心。如果变化过程不是以图层中心为参照的,比如旋转是以图层左边为旋转轴,那么就要对anchorPoint设置为(0,0.5),这时候就用第(2)种用法。所以为了避免混淆和思路混乱,只要在transform过程中不需要指定图层锚点的,一律用frame就好了。
CATransform3D
CATransform3D(三维变换矩阵)
CATransform3D 的数据结构定义了一个同质的三维变换(4x4 CGFloat值的矩阵),用于图层的旋转,缩放,偏移,歪斜和应用的透视。CATransform3D的结构体定义及各成员变量的职能如下:
struct CATransform3D
{
CGFloat m11(x缩放), m12(y切变), m13(旋转), m14();
CGFloat m21(x切变), m22(y缩放), m23(), m24();
CGFloat m31(旋转), m32( ), m33(), m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义);
CGFloat m41(x平移), m42(y平移), m43(z平移),m44();
};
transform
CALayer的属性transform,CATransform3D类型,用于以图层本身的anchorPoint为参考点来对图层本身和图层上的子图层进行变化。
sublayerTransform
CALayer的属性transform,CATransform3D类型,用于以图层本身的anchorPoint为参考点来对图层上的子图层进行变化,不包括图层本身。
CATransform3DIdentity
单位矩阵,该矩阵没有缩放,旋转,歪斜,透视。该矩阵应用到图层上,就是设置默认值。
Translation(平移)
CATransform3D CATransform3DMakeTranslation (CGFloat tx, CGFloat ty, CGFloat tz)
官方文档: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].即返回一个4x4的矩阵,将该矩阵立起来后看:
1 0 0 0
0 1 0 0
0 0 1 0
tx ty tz 1
对应CATransform3D的公式,tx、ty、tz参数分别用于x平移、y平移、z平移。x、y的平移比较好理解,对于tz来说,值越大,那么图层就越往外(接近屏幕),值越小,图层越往里(屏幕里)。
tx:X轴偏移位置,往下为正数。
ty:Y轴偏移位置,往右为正数。
tz:Z轴偏移位置,往外为正数。
CATransform3D CATransform3DTranslate ( CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz );
在t的基础上叠加变换,前面的方法则不能叠加,每次变换都是以起始状态为基础。
Scale(缩放)
CATransform3D CATransform3DMakeScale ( CGFloat sx, CGFloat sy, CGFloat sz );
官方文档L: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]. 返回的矩阵为:
sx 0 0 0
0 sy 0 0
0 0 sz 0
0 0 0 1
sx、sy、sz参数对应x、y、z轴的比例缩放,>0为正向比例缩放,<0为反向比例缩放。
CATransform3D CATransform3DScale ( CATransform3D t, CGFloat sx, CGFloat sy, CGFloat sz );
和平移一样,在t 的基础上叠加变换。
Rotation(旋转)
CATransform3D CATransform3DMakeRotation ( CGFloat angle, CGFloat x, CGFloat y, CGFloat z );
angle:旋转的弧度,所以要把角度转换成弧度:角度 * M_PI / 180
x:向X轴方向旋转。值范围-1 — 1之间
y:向Y轴方向旋转。值范围-1 — 1之间
z:向Z轴方向旋转。值范围-1 — 1之间
旋转方向:(当m34为负时)
旋转遵循左手定则。以绕y轴旋转为例,当参数y为正时,左手大拇指指向y轴正向(向下),手掌弯曲方向即为旋转方向,此时从上往下看是逆时针方向。若y参数为父,将大拇指指向y轴负向(向上),此时从上往下看是顺时针方向。 绕x轴z轴旋转判断方法相同。
注意
:若m34为正,饶x,y轴旋转遵循右手定则,绕z轴遵循左手定则。
self.superLayer = [CALayer layer];
self.superLayer.frame = CGRectMake(100, 200, 200, 100);
self.superLayer.backgroundColor = [UIColor orangeColor].CGColor;
[self.view.layer addSublayer:self.superLayer];
self.myLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 50)];
self.myLabel.backgroundColor = [UIColor greenColor];
self.myLabel.textAlignment = NSTextAlignmentCenter;
self.myLabel.text = @"测试";
self.myLabel.font = [UIFont systemFontOfSize:20.0];
self.myLabel.textColor = [UIColor purpleColor];
[self.superLayer addSublayer:self.myLabel.layer];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1/500.0;
self.myLabel.layer.transform = transform;
self.myLabel.layer.transform = CATransform3DRotate(self.myLabel.layer.transform, 60*M_PI/180, 1, 0, 0);//沿x轴正轴旋转60度
// self.myLabel.layer.transform = CATransform3DRotate(self.myLabel.layer.transform, 60*M_PI/180, 0, 1, 0);//沿y轴正轴旋转60度
// self.myLabel.layer.transform = CATransform3DRotate(self.myLabel.layer.transform, 60*M_PI/180, 0, 0, 1);//沿z轴正轴旋转60度
CATransform3D CATransform3DRotate ( CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z );
同平移/缩放,理解为效果的叠加。
直接修改矩阵或通过KVC设置变换效果
(1).可以直接修改矩阵的成员变量来得到一个指定的变幻效果。
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
}
常用的则是对m34赋值,以实现透视投影近大远小的效果。 Ps: m34= -1/D, 默认值是0,也就是说D无穷大,这是看上去没有近大远小的效果,也就是正交投影。D越小,透视效果越明显。
(2).通过KVC对layer直接设置。
例如设置绕x正轴旋转60度:
[self.myLabel.layer setValue:[NSNumber numberWithFloat:60*M_PI/180] forKeyPath:@"transform.rotation.y"];
相关关键字路径如下:
其他方法
CATransform3DMakeAffineTransform
CGAffineTransform CATransform3DGetAffineTransform (CATransform3D t);
bool CATransform3DIsAffine (CATransform3D t);
仿射效果。
就是CATransform3D对象和CGAffineTransform对象之间的互相转化。
bool CATransform3DIsIdentity (CATransform3D t);
bool CATransform3DEqualToTransform (CATransform3D a, CATransform3D b);
比较一个变换矩阵是否是单位矩阵,以及两个矩阵是否相等。