【转】 CATransform3D 矩阵变换之立方体旋转实现细节

原文网址:http://blog.csdn.net/ch_soft/article/details/7351896

第一部分、前几天做动画,使用到了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<m33<1,图形整体放大,若m33<0,发生关于原点的对称等比变换。
  • 单设m12或m21的时候是切变效果,当【m12=角度】和【m21=-角度】的时候就是旋转效果了。两个角度值相同。
  • ()空的地方以后补充。
  • 还有,要想使用CATransform3D,必须在工程里导入QuartzCore.framework。然后在文件中

#import <QuartzCore/CATransform3D.h>。

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结构成员的意义。

structCATransform3D
{
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<m33<1,图形整体放大,若s<0,发生关于原点的对称等比变换。

()空的地方以后补充。

2. CATransform3DMakeTranslation

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

3. CATransform3DMakeRotation And CATransform3DRotate

CATransform3DMakeRotation()

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

  

4. 翻转的动画

  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. if ( direction == RotateFromBottom )
  14. {
  15. // 创建(某方向)关键帧动画.
  16. translationY = [CABasicAnimation animationWithKeyPath:
  17. @"sublayerTransform.translation.y"];
  18. translationY.toValue = [NSNumber numberWithFloat:-(height / 2)];【2】
  19. rotation = [CABasicAnimation animationWithKeyPath:
  20. @"sublayerTransform.rotation.x"];
  21. rotation.toValue = [NSNumber numberWithFloat:D2R(-90.0f)];
  22. }
  23. else if ( direction == RotateFromTop )
  24. {
  25. }
  26. // 处理Z轴
  27. translationZ = [CABasicAnimation animationWithKeyPath:
  28. @"sublayerTransform.translation.z"];
  29. translationZ.toValue = [NSNumber numberWithFloat:height / 2];【3】
  30. animationGroup.animations =
  31. [NSArray arrayWithObjects: rotation, translationY, translationZ, nil nil];
  32. animationGroup.fillMode = kCAFillModeForwards;
  33. animationGroup.removedOnCompletion = NO;
  34. [_transformedLayer addAnimation:animationGroup forKey:kAnimationKey];
  35. }

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对象。但是它根据提供的转换类型来渲染。

上一篇:java基础知识回顾之---java String final类 容易混淆的java String常量池内存分析


下一篇:OTA升级详解(三)