【轉】如何在IOS中使用3D UI - CALayer的透視投影


原文網址:http://www.tairan.com/archives/2041/

例子代碼可以在 http://www.tairan.com/thread-3607-1-1.html 下載

    iOS的UI是基於UIView類的,我們能看到的每個UI元素都是UIView或者UIView的子類。View按樹形結構組織起來,樹根是UIWindow。

     View負責界面的交互和顯示,其中顯示部分由CALayer來完成。每個UIView包含一個CALayer實例。可以這么認為,UIView本身是不可見的,我們能看到的都是CALayer,UIView只是負責對CALayer進行管理。

    UIView的顯示設置都是對CALayer屬性的封裝,但是這層封裝掩蓋了CALayer提供的3D顯示功能。所以我們想讓UIView顯示3D的效果的話,需要直接操作CALayer。

    要操作CALayer對象,首先要在工程中包含QuartzCore.framework,在文件中import <QuartzCore/QuartzCore.h>頭文件。QuartzCore.framework中包含了CALayer以及CALayer一些官方子類的定義。

    通過設置CALayer的transform屬性,可以使CALayer產生3D空間內的平移、縮放、旋轉等變化。

    第一個例子,繞坐標軸的旋轉:

    原始場景如圖

    

 

    使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6001); 繞Z軸旋轉30度后的效果

    

    使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6, 0, 1, 0); 繞Y軸旋轉30度后的效果

     

    使用 image.layer.transform = CATransform3DMakeRotation(M_PI/6, 10, 0); 繞X軸旋轉30度后的效果

    

    能夠發現,繞Z軸的旋轉比較符合預期,但是繞X軸Y軸的旋轉只是在Y軸X軸上進行了一些縮放而已。這是因為,在CALayer的顯示系統中,默認的相機使用正交投影,正交投影沒有遠小近大效果,所以在本例中,只能造成相應軸上的縮放。

    第二個例子,透視投影

    CALayer默認使用正交投影,因此沒有遠小近大效果,而且沒有明確的API可以使用透視投影矩陣。所幸可以通過矩陣連乘自己構造透視投影矩陣。構造透視投影矩陣的代碼如下:

CATransform3D CATransform3DMakePerspective(CGPoint center, float disZ)
{        
    CATransform3D transToCenter = CATransform3DMakeTranslation(-center.x, -center.y, 0);
    CATransform3D transBack = CATransform3DMakeTranslation(center.x, center.y, 0);  
    CATransform3D scale = CATransform3DIdentity;    
    scale.m34 = -1.0f/disZ;    
    return CATransform3DConcat(CATransform3DConcat(transToCenter, scale), transBack);    
}
    CATransform3D CATransform3DPerspect(CATransform3D t, CGPoint center, float disZ)    
{    
    return CATransform3DConcat(t, CATransform3DMakePerspective(center, disZ));    
}

    這個函數的實現原理要參考計算機圖形學的3D變換部分,以后再做解釋。現在只需要了解接口的含義,center指的是相機 的位置,相機的位置是相對於要進行變換的CALayer的來說的,原點是CALayer的anchorPoint在整個CALayer的位置,例如CALayer的大小是(100, 200), anchorPoint值為(0.5, 0.5),此時anchorPoint在整個CALayer中的位置就是(50, 100),正中心的位置。傳入透視變換的相機位置為(0, 0),那么相機所在的位置相對於CALayer就是(50, 100)。如果希望相機在左上角,則需要傳入(-50, -100)。disZ表示的是相機離z=0平面(也可以理解為屏幕)的距離。

    帶透視效果的旋轉,效果如下:

    

 

CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 1, 0, 0);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);

    

    

CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 0, 1, 0);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);

    

    

CATransform3D rotate = CATransform3DMakeRotation(M_PI/6, 0, 0, 1);
image.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);

    image的默認anchorPoint為(0.5,0.5),也就是在圖片中心;眼睛在圖片中心點,距屏幕200單位。可以觀察到,因為翻轉,使圖片的不同部分離屏幕距離不同,近大遠小的效果使立體感大大提升。饒Z軸的旋轉不因為透視產生變化,因為所有的點離屏幕距離相同,所以不會產生近大遠小的透視感。

    第三,更多的效果。CALayer的旋轉和縮放是繞anchorPoint點的,改變anchorPoint的值,可以使Layer繞不同的點而不只是中心點旋轉縮放。在構造透視投影矩陣的例子中就可以看到,CATransform3D可以使用CATransform3DConcat函數連接起來以構造更復雜的變換。通過這些方法,可以組合出更多的效果來。下面是個翻轉的動畫。使用UITimer

    

 

- (void)update
{
static float angle = 0;
angle += 0.05f;
CATransform3D transloate = CATransform3DMakeTranslation(0, 0, -200);
CATransform3D rotate = CATransform3DMakeRotation(angle, 0, 1, 0);
CATransform3D mat = CATransform3DConcat(rotate, transloate);
image.layer.transform = CATransform3DPerspect(mat, CGPointMake(0, 0), 500);
}

    最后是兩個更復雜的例子。第一個是使用四張同樣大小的圖片圍成一個框,讓這個框動畫旋轉。

    

    

CATransform3D move = CATransform3DMakeTranslation(0, 0, 160);
CATransform3D back = CATransform3DMakeTranslation(0, 0, -160);
CATransform3D rotate0 = CATransform3DMakeRotation(-angle, 0, 1, 0);
CATransform3D rotate1 = CATransform3DMakeRotation(M_PI_2-angle, 0, 1, 0);
CATransform3D rotate2 = CATransform3DMakeRotation(M_PI_2*2-angle, 0, 1, 0);
CATransform3D rotate3 = CATransform3DMakeRotation(M_PI_2*3-angle, 0, 1, 0);
CATransform3D mat0 = CATransform3DConcat(CATransform3DConcat(move, rotate0), back);
CATransform3D mat1 = CATransform3DConcat(CATransform3DConcat(move, rotate1), back);
CATransform3D mat2 = CATransform3DConcat(CATransform3DConcat(move, rotate2), back);
CATransform3D mat3 = CATransform3DConcat(CATransform3DConcat(move, rotate3), back);
image0.layer.transform = CATransform3DPerspect(mat0, CGPointZero, 500);
image1.layer.transform = CATransform3DPerspect(mat1, CGPointZero, 500);
image2.layer.transform = CATransform3DPerspect(mat2, CGPointZero, 500);
image3.layer.transform = CATransform3DPerspect(mat3, CGPointZero, 500);

   上面的例子都使用UIImage的CALayer,但CALayer產生的動畫可以應用在所有的UIView及子類上,下面是個普通界面的立體翻轉效果。 

    

    

float dis = 160 * 1.732f;
CATransform3D move = CATransform3DMakeTranslation(0, 0, dis);
CATransform3D back = CATransform3DMakeTranslation(0, 0, -dis);
CATransform3D rotate0 = CATransform3DMakeRotation(-angle, 0, 1, 0);
CATransform3D rotate1 = CATransform3DMakeRotation(-angle + M_PI/3.0f, 0, 1, 0);
CATransform3D mat0 = CATransform3DConcat(CATransform3DConcat(move, rotate0), back);
CATransform3D mat1 = CATransform3DConcat(CATransform3DConcat(move, rotate1), back);
view0.layer.transform = CATransform3DPerspect(mat0, CGPointZero, 500);
view1.layer.transform = CATransform3DPerspect(mat1, CGPointZero, 500);


免責聲明!

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



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