CATransform3D 矩陣變換之立方體旋轉實現細節 (轉)


原文地址

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

第一部分、前幾天做動畫,使用到了CATransform3D ,由於沒有學過計算機圖形學,矩陣中m11--m44的各個含義都不清楚,經過幾天研究總結如下:(供和我一樣的菜鳥學習)

{
CGFloat m11(x縮放), m12(y切變), m13(), m14();
CGFloat m21(x切變), m22(y縮放), m23(), m24();
CGFloat m31(), m32(), m33(), m34(透視效果,要操作的這個對象要有旋轉的角度,否則沒有效果。當然,z方向上得有變化才會有透視效果);
CGFloat m41(x平移), m42(y平移), m43(z平移), m44();
};

ps:

  • 整體比例變換時,也就是m11==m22時,若m33>1,圖形整體縮小,若0<m33<1,圖形整體放大,若m33<0,發生關於原點的對稱等比變換。
  • 單設m12或m21的時候是切變效果,當【m12=角度】和【m21=角度】的時候就是旋轉效果了。兩個角度值相同。
  • ()空的地方以后補充。
  • 還有,要想使用CATransform3D,必須在工程里導入QuartzCore.framework。然后在文件中

#import <QuartzCore/CATransform3D.h>。

 iphone 透視效果(perspective)

    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = 0.0005; // 透視效果
    transform = CATransform3DRotate(transform,(M_PI/180*40), 0, 1, 0);
    [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()
[objc] view plaincopy

    _transformedLayer = [CALayer layer];  
    _transformedLayer.frame = self.bounds;  
    _transformedLayer.anchorPoint = CGPointMake(0.5f, 0.5f);  
    CATransform3D sublayerTransform = CATransform3DIdentity;  
    // Set perspective  
    sublayerTransform.m34 = kPerspective;  
    [_transformedLayer setSublayerTransform:sublayerTransform];  
      
    [self.layer addSublayer:_transformedLayer];  
    //init Sublayers  
    CATransform3D t = CATransform3DMakeTranslation(0, 0, 0);  
    // take snapshot of the current view  
    [_transformedLayer addSublayer:[self snapshot:t   
                                         withView:_contentView   
                                         isMasked:YES]];  
    // 暫時先支持一個方向翻轉  
    RotateDirection direction = RotateFromBottom;  
    if (YES || direction == RotateFromBottom)  
    {  
        CGFloat height = self.bounds.size.height;  
        //CGFloat cubeSize = 100.0f;  
        t = CATransform3DRotate(t, D2R(90.0), 1, 0, 0);【1】  
        t = CATransform3DTranslate(t, 0, height, 0);  
        CALayer *subLayer = [self snapshot:t withView:view isMasked:YES];  
        [_transformedLayer addSublayer:subLayer];  
    }  
    else   
    {  
    }  
      
    _newContentView = view;  
      
    [self animationCubeRotate:direction withDuration:duration];  

  

4. 翻轉的動畫
[objc] view plaincopy

    - (void)animationCubeRotate:(RotateDirection)direction   
                   withDuration:(float)duration  
    {  
        [CATransaction flush];  
        CGFloat height = self.bounds.size.height;  
        CABasicAnimation *rotation;  
        // CABasicAnimation *translationX;  // 如果沿X軸翻轉,則用不到這個變量.  
        CABasicAnimation *translationY; // 如果沿Y軸翻轉,則用不到這個變量.  
        CABasicAnimation *translationZ;  
        CAAnimationGroup *animationGroup = [CAAnimationGroup animation];  
        animationGroup.delegate = self;  
        animationGroup.duration = duration;  
          
        if ( direction == RotateFromBottom )  
        {  
            // 創建(某方向)關鍵幀動畫.  
            translationY = [CABasicAnimation animationWithKeyPath:  
                            @"sublayerTransform.translation.y"];  
            translationY.toValue = [NSNumber numberWithFloat:-(height / 2)];【2】  
            rotation = [CABasicAnimation animationWithKeyPath:  
                        @"sublayerTransform.rotation.x"];  
            rotation.toValue = [NSNumber numberWithFloat:D2R(-90.0f)];  
        }   
        else if ( direction == RotateFromTop )  
        {  
        }  
          
        // 處理Z軸  
        translationZ = [CABasicAnimation animationWithKeyPath:  
                        @"sublayerTransform.translation.z"];  
        translationZ.toValue = [NSNumber numberWithFloat:height / 2];【3】  
        animationGroup.animations =   
            [NSArray arrayWithObjects: rotation, translationY, translationZ, nil nil];  
        animationGroup.fillMode = kCAFillModeForwards;  
        animationGroup.removedOnCompletion = NO;  
        [_transformedLayer addAnimation:animationGroup forKey:kAnimationKey];  
    }  

made, 我發現這個東西確實很難講清楚,主要是因為我理論薄弱,

【1】針對X軸旋轉,就是1,00,針對Y軸旋轉,就是0,10...下面那行也要進行正確的轉換。

【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 = 453.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對象。但是它根據提供的轉換類型來渲染。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM