Core Animation之CATransform3D:矩阵变换3D旋转

 

关于图层的几个坐标系。

对于ios来说,坐标系的(0,0)点在左上角,就是越往下,Y值越大。越往右,X值越大。

一个图层的frame,它是position,bounds,anchorPoint和transform属性的一部分。

设置一个新的frame将会相应的改变图层的position和bounds,但是frame本身并没有保存。


position:是一个CGPoint值,它指定图层相当于它父图层的位置,该值基于父图层的坐标系。

bounds:是一个CGRect值,指定图层的大小(bounds.size)和图层的原点(bounds.origin),这个坐标系是基于自身的。如果改变bounds的origin,那么在该图层的子图层,坐标会跟着改变。也就是说,改变自身的坐标系,本身在父图层的位置不变,但它上的子图层位置变化。

anchorPoint:是一个CGPoint值,它是指定了一个基于bounds的符合坐标系的位置。锚点(anchorpoint)制定了bounds相对于position的值,同时也作为一个变化时候的中心点。锚点使用空间坐标系取值范围是0-1之间的数。默认是0.5,也就是秃瓢的中心点,如果是(0,0)那么,图层向左上方移动。如果是(1,1)就向右下方移动。


看下面的两个图,就能够够清晰的看出锚点变化所带来的不一样。(此图为MACOS 坐标系,如果是IOS,那么(0,0)点在图的左上方。)




对于anchorPoint的解释在ios中如图:

下图中的红点位置就是锚点的位置:默认为(0.5,0.5)。在对图像进行变换时,都是按照这个点来进行缩放,偏移等。

一旦修改锚点的位置为:(0,0),那么图像就会变成下图。各种变换就会按照这个点来运动。

所以说,在ios系统中,锚点的坐标系是:左上角为(0,0),右下角为(1,1)。

根据此图,在理解上面的定义,就直观了一些。

//

//

//

 

图层的几何变换

可以通过矩阵来改变一个图层的几何形状。

CATransform3D的数据结构定义了一个同质的三维变换(4x4CGFloat值的矩阵),用于图层的旋转,缩放,偏移,歪斜和应用的透视。


图层的2个属性指定了变换矩阵:transform和 sublayerTransform。

transform : 是结合anchorPoint(锚点)的位置来对图层和图层上的子图层进行变化。

sublayerTransform:是结合anchorPoint(锚点)的位置来对图层的子图层进行变化,不包括本身。


CATransform3DIdentity是单位矩阵,该矩阵没有缩放,旋转,歪斜,透视。该矩阵应用到图层上,就是设置默认值。

//

//

//

变换函数

CATransform3DMakeTranslation 

官方文档:

Returns atransform that translates by '(tx, ty, tz)'. t' = [1 0 0 0; 0 1 00; 0 0 1 0; tx ty tz 1].

CATransform3D CATransform3DMakeTranslation (CGFloat tx, CGFloat ty,CGFloat tz)。

做一个解释,对于初学者来说,可能没有看明白是什么意思。我详细说下

对于CATransform3D来说,它是一个4x4CGFloat的矩阵。

而上面给的值:[10 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1].

给竖起来后。就发现:

1   0    0   0

0   1    0   0

0   0   1    0

tx ty   tz  1

竖起来看就很明显了。

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();
};

根据这个公式,就一目了然了。

 

CATransform3DCATransform3DMakeTranslation (CGFloat tx, CGFloat ty, CGFloattz)

的参数意思就是 

tx::x平移。 ty:y平移。  tz:z平移

//
//
//

换换函数:

CATransform3DCATransform3DMakeTranslation (CGFloat tx, CGFloat ty, CGFloattz)

tx:X轴偏移位置,往下为正数。

ty:Y轴偏移位置,往右为正数。

tz:Z轴偏移位置,往外为正数。

例:
如果有2个图层,一个是绿色的,一个是红色的。先加载绿色,后加载红色。

tx,ty的左右偏移先不说了。

如果绿色的tz为-10 ,红色的tz为 0 效果如下。


如果绿色的tz为 0 ,红色的tz为-10 效果如下。

对于tz来说,值越大,那么图层就越往外(接近屏幕),值越小,图层越往里(屏幕里)。


CATransform3D CATransform3DTranslate (CATransform3D t, CGFloat tx,CGFloat ty, CGFloat tz);

t:就是上一个函数。其他的都一样。

就可以理解为:函数的叠加,效果的叠加。



CATransform3D CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz);

sx:X轴缩放,代表一个缩放比例,一般都是0 --- 1 之间的数字。

sy:Y轴缩放。

sz:整体比例变换时,也就是m11(sx)==m22(sy)时,若m33(sz)>1,图形整体缩小,若0<1,图形整体放大,若m33(sz)<0,发生关于原点的对称等比变换。


当sx = 1,sy =1时。如图:

当sx = 0.5,sy =0.5时。如图:



CATransform3D CATransform3DScale(CATransform3D t, CGFloat sx, CGFloat sy, CGFloatsz)

 

t:就是上一个函数。其他的都一样。

就可以理解为:函数的叠加,效果的叠加。



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之间


例:向X轴旋转60度。           向Y轴旋转60度。                 向Z轴旋转60度。

                    

向X轴,Y轴都旋转60度,就是沿着对角线旋转。


可以通过X,Y,Z轴同时变化,来旋转图像。


CATransform3D CATransform3DRotate (CATransform3D t, CGFloat angle,CGFloat x, CGFloat y, CGFloat z);

 

t:就是上一个函数。其他的都一样。

就可以理解为:函数的叠加,效果的叠加。



CATransform3D CATransform3DInvert (CATransform3D t);

翻转效果。

               未使用                         使用

  


CGAffineTransform CATransform3DGetAffineTransform (CATransform3Dt);

bool CATransform3DIsAffine (CATransform3D t);

仿射效果。


就是把一个 CATransform3D 对象转换成一个 CGAffineTransform 对象。

也就是把 CATransform3D 矩阵转换成 CGAffineTransform 矩阵


 

变换函数同时提供了可以比较一个变换矩阵是否是单位矩阵,或者两个矩阵是否相等。

bool CATransform3DIsIdentity (CATransform3D t);

boolCATransform3DEqualToTransform (CATransform3D a, CATransform3Db);



也可以通过修改数据结构和键值来设置变换效果。

 

struct CATransform3D
               {

                 CGFloat m11, m12, m13, m14;

                           CGFloat m21, m22, m23, m24;

                           CGFloat m31, m32, m33, m34;

                                  CGFloat m41, m42, m43, m44;
}
可以直接修改 其中的一个值,来达到相同的效果。
或者修改键值
[myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];






3D,顾名思义就是可以在z轴上动作,这里列举三个用例


//用例1 scale 
 CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:@" transform"];
    CATransform3D transform =  CATransform3DMakeScale(0.5, 0.5, 1.0);   //x,y,z放大缩小倍数
    NSValue *value = [NSValue valueWithCATransform3D:transform];
    [theAnimation setToValue:value];
    
    transform = CATransform3DMakeScale(1.0, 1.0, 1.0);
    value = [NSValue valueWithCATransform3D:transform];
    [theAnimation setFromValue:value];

    [theAnimation setAutoreverses:YES];   //原路返回的动画一遍
    [theAnimation setDuration:1.0];
    [theAnimation setRepeatCount:2];
    
   [layer addAnimation:theAnimation forKey:nil];


//用例2 rotate 
   ......
 CATransform3D transform =  CATransform3DMakeRotation(1.57, 1, 1, 0);  //1.57表示所转角度的弧度 = 90Pi/180 = 90*3.14/180
    NSValue *value = [NSValue valueWithCATransform3D:transform];
    [theAnimation setToValue:value];
    
    transform = CATransform3DMakeRotation(0, 1, 1, 0);
    value = [NSValue valueWithCATransform3D:transform];
    [theAnimation setFromValue:value];
  ......

//用例3 scale+rotate+position
 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@" transform"];
    CATransform3D rotateTransform = CATransform3DMakeRotation(1.57, 0, 0, -1);
    CATransform3D scaleTransform = CATransform3DMakeScale(5, 5, 5);
    CATransform3D positionTransform =  CATransform3DMakeTranslation(0, 0, 0);  //位置移动
    CATransform3D combinedTransform = CATransform3DConcat(rotateTransform, scaleTransform);  //Concat就是combine的意思
    combinedTransform = CATransform3DConcat(combinedTransform, positionTransform);  //再combine一次把三个动作连起来
   
    [anim setFromValue:[NSValue valueWithCATransform3D: CATransform3DIdentity]];  //放在3D坐标系中最正的位置
    [anim setToValue:[NSValue valueWithCATransform3D:combinedTransform]];
    [anim setDuration:5.0f];
    
    [ layer addAnimation:anim forKey:nil];
  
    [layer setTransform:combinedTransform];   //如果没有这句,layer执行完动画又会返回最初的state



第一部分、前几天做动画,使用到了CATransform3D ,由于没有学过计算机图形学,矩阵中m11--m44的各个含义都不清楚,经过几天研究总结如下:(供和我一样的菜鸟学习)

  1. struct CATransform3D
  2. {
  3. CGFloat m11(x缩放), m12(y切变), m13(), m14() ;
  4. CGFloat m21(x切变), m22(y缩放), m23(), m24() ;
  5. CGFloat m31(), m32(), m33(), m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。当然,z方向上得有变化才会有透视效果) ;
  6. CGFloat m41(x平移), m42(y平移), m43(z平移), m44() ;
  7. } ;

ps:

  • 整体比例变换时,也就是m11==m22时,若m33>1,图形整体缩小,若0
  • 单设m12或m21的时候是切变效果,当【m12=角度】和【m21=角度】的时候就是旋转效果了。两个角度值相同。
  • ()空的地方以后补充。
  • 还有,要想使用CATransform3D,必须在工程里导入QuartzCore.framework。然后在文件中

#import

 

iphone 透视效果(perspective)

  1. CATransform3D transform  = CATransform3DIdentity ;
  2. transform. m34  =  0.0005 ;  // 透视效果
  3. transform  = CATransform3DRotate (transform , (M_PI / 180 * 40 ) ,  0 ,  1 ,  0 ) ;
  4. [piece. layer setTransform :transform ] ;

第二行一定要写在第三行的前面!自己理解!

第二部分

1. 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();
};

ps:整体比例变换时,也就是m11==m22时,若m33>1,图形整体缩小,若0

()空的地方以后补充。

2. CATransform3DMakeTranslation

CATransform3DMakeTranslation(0, 0, 0) 创建了一个4*4的单位矩阵。

3. CATransform3DMakeRotation And CATransform3DRotate

CATransform3DMakeRotation()

[objc] view plain copy
  1. _transformedLayer = [CALayer layer];  
  2. _transformedLayer.frame = self.bounds;  
  3. _transformedLayer.anchorPoint = CGPointMake(0.5f0.5f);  
  4. CATransform3D sublayerTransform = CATransform3DIdentity;  
  5. // Set perspective  
  6. sublayerTransform.m34 = kPerspective;  
  7. [_transformedLayer setSublayerTransform:sublayerTransform];  
  8.   
  9. [self.layer addSublayer:_transformedLayer];  
  10. //init Sublayers  
  11. CATransform3D t = CATransform3DMakeTranslation(000);  
  12. // take snapshot of the current view  
  13. [_transformedLayer addSublayer:[self snapshot:t   
  14.                                      withView:_contentView   
  15.                                      isMasked:YES]];  
  16. // 暂时先支持一个方向翻转  
  17. RotateDirection direction = RotateFromBottom;  
  18. if (YES || direction == RotateFromBottom)  
  19. {  
  20.     CGFloat height = self.bounds.size.height;  
  21.     //CGFloat cubeSize = 100.0f;  
  22.     t = CATransform3DRotate(t, D2R(90.0), 100);【1】  
  23.     t = CATransform3DTranslate(t, 0, height, 0);  
  24.     CALayer *subLayer = [self snapshot:t withView:view isMasked:YES];  
  25.     [_transformedLayer addSublayer:subLayer];  
  26. }  
  27. else   
  28. {  
  29. }  
  30.   
  31. _newContentView = view;  
  32.   
  33. [self animationCubeRotate:direction withDuration:duration];  

  

4. 翻转的动画

[objc] view plain copy
  1. - (void)animationCubeRotate:(RotateDirection)direction   
  2.                withDuration:(float)duration  
  3. {  
  4.     [CATransaction flush];  
  5.     CGFloat height = self.bounds.size.height;  
  6.     CABasicAnimation *rotation;  
  7.     // CABasicAnimation *translationX;  // 如果沿X轴翻转,则用不到这个变量.  
  8.     CABasicAnimation *translationY; // 如果沿Y轴翻转,则用不到这个变量.  
  9.     CABasicAnimation *translationZ;  
  10.     CAAnimationGroup *animationGroup = [CAAnimationGroup animation];  
  11.     animationGroup.delegate = self;  
  12.     animationGroup.duration = duration;  
  13.       
  14.     if ( direction == RotateFromBottom )  
  15.     {  
  16.         // 创建(某方向)关键帧动画.  
  17.         translationY = [CABasicAnimation animationWithKeyPath:  
  18.                         @"sublayerTransform.translation.y"];  
  19.         translationY.toValue = [NSNumber numberWithFloat:-(height / 2)];【2】  
  20.         rotation = [CABasicAnimation animationWithKeyPath:  
  21.                     @"sublayerTransform.rotation.x"];  
  22.         rotation.toValue = [NSNumber numberWithFloat:D2R(-90.0f)];  
  23.     }   
  24.     else if ( direction == RotateFromTop )  
  25.     {  
  26.     }  
  27.       
  28.     // 处理Z轴  
  29.     translationZ = [CABasicAnimation animationWithKeyPath:  
  30.                     @"sublayerTransform.translation.z"];  
  31.     translationZ.toValue = [NSNumber numberWithFloat:height / 2];【3】  
  32.     animationGroup.animations =   
  33.         [NSArray arrayWithObjects: rotation, translationY, translationZ, nil nil];  
  34.     animationGroup.fillMode = kCAFillModeForwards;  
  35.     animationGroup.removedOnCompletion = NO;  
  36.     [_transformedLayer addAnimation:animationGroup forKey:kAnimationKey];  
  37. }  

made, 我发现这个东西确实很难讲清楚,主要是因为我理论薄弱,

【1】针对X轴旋转,就是1,0,0,针对Y轴旋转,就是0,1,0...下面那行也要进行正确的转换。

【2】此处应该是和 anchorPoint有关系的。

【3】这个值会影响类似于深度的东西,比如说Cube会离我们更近,或者是更远。(但是,似乎不算是透视关系)


第三部分、实例开发:
一般情况下我们不必对IOS窄哦的这个矩阵进行直接操作,SDK为我们提供了现成的转换方法。这里我们将使用core animation 利用这个矩阵进行立方体旋转的实现,这是我从网上down下来代码之后,自己进行摸索后总结的笔记,难免有不足之处,希望没有误导看到本文的同学。

这里用到的转换喊着主要包括旋转函数和移动函数 : CATransform3DRotate CATransform3DMakeTranslation

原理说明:
旋转类型:从当前显示旋转到顶部
具体操作:1.将旋转分为一段段的,组成路径,由CAKeyframeAnimation保存,每一段都由旋转的角度确定,比如角度为a(旋转完成应该是有90度); 2.将当前视图(后加入的)进行偏移变换,产生一个偏移矩阵,也就是将视图中心点转移到旋转该角度后预定位置;  3.将上面的结果进行a角度旋转变换;
说明:旋转的方式有:从当前向顶上,向底部,向左,向右,以及从这些所有位置旋转回来。只是形成立方体旋转的基本组成方式。由于所有方式过程相似,只要修改旋转轴(后文会提到)和偏移即可,所以只是叙述其中的一个。

具体实现:
1.加入两个子view
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
view.backgroundColor = [UIColor redColor];
view.center = self.view.center;
[self.view addSubview:view];

UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
view1 .backgroundColor = [UIColor redColor];
view1 .center = self.view.center;
[self.view addSubview:view1];

2.进行旋转
某次旋转的核心变换:(重点理解两次变换,为什么要这么变?需要一定的空间几何思维)
CGFloat radius = DEGREE_TO_RADIUS(90*(float)(count - 1 - index)/(float)(count -1));//计算旋转的角度;其中count是旋转路径节点数目,index是第几个节点  (一共旋转90度)                  
CATransform3D transform3d = CATransform3DMakeTranslation(0, -r*sinf(radius), -r*(1-cosf(radius)));//计算偏移矩(表示在Y轴上偏移量为-r*sinf(radius),Z轴上为后者)
transform3d = CATransform3DRotate(transform3d, radius, 1.f, 0, 0);//在上述基础上计算旋转矩阵
NSValue *value = [NSValue valueWithCATransform3D:transform3d];
[values addObject:value];//将矩阵值加入路径中
if (index > 0) {
[timingFunctions addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];//将时间控制加入

NOTE:CATransform3DRotate()方法制造旋转矩阵,控制旋转角度和方向。这里有一个诀窍就是向量值某个坐标值的正负影响向量的指向方向也影响视图的旋转方向。

创建路径:
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
NSMutableArray *values = [NSMutableArray array]; //矩阵表示的路径
NSMutableArray *timingFunctions = [NSMutableArray array];//对应的时间控制
for (int i = 0; i< count; i ++)

     //执行上面的代码快

animation.values = values;
animation.timingFunctions = timingFunctions;
animation.duration = duration;

3.至此,路径创建成功,最难的部分已经过去。继续我们的旅程:
创建 CAAnimationGroup :
CAAnimationGroup *cubicAnimation = [CAAnimationGroup animation];
cubicAnimation.duration = duration;
cubicAnimation.delegate = delegate;
cubicAnimation.animations = [NSArray arrayWithObjects:transform,nil]; //transform就是上一个部分我们辛苦创造的CAKeyframeAnimation *animation

4.组装,实现动画
CATransform3D transform = CATransform3DIdentity;
[self.layer setSublayerTransform:transform]; //还原3D设置(没有试过不还原,官方要求必须还原)
[CATransaction begin];
[CATransaction setAnimationDuration:duration]; //设置动画运行时长
[CATransaction setCompletionBlock:completionBlock]; //完成后执行的动作
[subViewIn.layer addAnimation:inAnimation forKey:@"cubeIn"]; //在层里加入动画,系统会自动将他开始
[CATransaction commit];


5. 重要:最后的最后,黎明前总是最黑暗的时刻,这次也不例外。我们所做的只是将一个面转了一下,要想形成立方体效果,我们还得把另外的面跟着做对应旋转。晕了吧,慢慢回来,再苦逼的做一遍工作吧。不过幸好我们有电脑帮我们做那些烦人的重复的事情,写个函数体,copy,修改参数值,OK!

第四部分
CATransform3D myTransform;
myTransform = CATransform3DMakeRotation(angle, x, y, z);

该CATransform3DMakeRotation函数创建了一个转变,将在三维轴坐标系以任意弧度旋转层。x-y-z轴的有个确定的范围(介于-1 和+1之间) 。相应的坐标轴指定的值告诉系统在该轴上旋转。例如,如果X轴是设置为-1或1 ,该对象将的X轴的方向上旋转,这意味着将把它垂直旋转。把这些值看做是插入在图像每个坐标轴上的秸秆(Think of these values as inserting straws through the image for each axis.)。如果秸秆插过x轴,图像将沿着秸秆垂直旋转。您可以使用坐标轴角度值创建更复杂的旋转。。对于大多数的用途,但是,值介于-1和+1已经足够。

要水平(垂直)旋转45度,您可以使用下面的代码:

myTransform = CATransform3DMakeRotation(0.78, 1.0, 0.0, 0.0);

要在Y轴上旋转相同的值:
myTransform = CATransform3DMakeRotation(0.78, 0.0, 1.0, 0.0);


0.78 ,用在前面的例子,是由角度值经计算转化为弧度值。要把角度值转化为弧度值,可以使用一个简单的公式Mπ/180 。例如, 45π/180 = 45 ( 3.1415 ) / 180 = 0.7853 。如果你打算在你的程序里面一直都用角度值的话,你可以写一个简单的转化方法,以帮助保持您的代码是可以理解的:

double radians(float degrees) {
    return ( degrees * 3.14159265 ) / 180.0;
}

当你创建一个转换的时候,你将要调用这个方法:

myTransform = CATransform3DMakeRotation(radians(45.0), 0.0, 1.0, 0.0);

当变换(transformation)被创建好了以后,应用在你正在操作的层上。CALayer对象提供了一个transform属性来连接转换。层将执行分配给transform属性的转换:

imageView.layer.transform = myTransform;

当对象被显示后,将会显示应用到它的转换效果。在你的代码中,你任然把它当做是个2D对象。但是它根据提供的转换类型来渲染。



参考:http://blog.sina.com.cn/s/blog_8f5097be0101b91z.html

http://blog.csdn.net/yongyinmg/article/details/37931537

http://blog.csdn.net/ch_soft/article/details/7351896

你可能感兴趣的:(笔记,iOS编程)