iOS核心動畫--3D顯示

仿射變換

實際上UIView 的transform屬性是一個CGAffineTransform類型,用于在二維空間做旋轉(zhuǎn),縮放和平
移。CGAffineTransform是一個可以和二維空間向量(例如 CGPoint )做乘法 的3X2的矩陣

image.png

用 CGPoint的每一列和 矩陣的每一行對應(yīng)元素相乘再求和,就形成了一個新的 類型的結(jié)果。要解釋一下圖中顯示的灰色元素,為了能讓矩陣做乘法,左邊矩陣的列數(shù)一定要和右邊矩陣的行數(shù)個數(shù)相同,所以要給矩陣填充一些標志值,使得既可以讓矩陣做乘法,又不改變運算結(jié)果,并且沒必要存儲這些添加的值,因為它們的值不會發(fā)生變化,但是要用來做運算。

當(dāng)對圖層應(yīng)用變換矩陣,圖層矩形內(nèi)的每一個點都被相應(yīng)地做變換,從而形成一個 新的四邊形的形狀。 CGAffineTransform中的“仿射”的意思是無論變換矩陣用什么值,圖層中平行的兩條線在變換之后任然保持平行, CGAffineTransform 可以做出任意符合上述標注的變換

image.png

Core Graphics提供了一系列函數(shù),對完全沒有數(shù)學(xué)基礎(chǔ)的開發(fā)者也能夠簡單地做一些變換。如下幾個函數(shù)都 創(chuàng)建了一個 CGAffineTransform 實例:

CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)

旋轉(zhuǎn)和縮放變換都可以很好解釋--分別旋轉(zhuǎn)或者縮放一個向量的值。平移變換是指 每個點都移動了向量指定的x或者y值--所以如果向量代表了一個點,那它就平移了 這個點的距離。

UIView可以通過設(shè)置transform屬性做變換,但實際上它只是封裝了內(nèi)部圖層的變換。

CALayer同樣也有一個transform的屬性,但它的類型是 CATransform3D,而不是CGAffineTransform。CALayer對應(yīng)于UIView的transform屬性叫做affineTransform。

- (void)viewDidLoad
{
    [super viewDidLoad];
    //rotate the layer 45 degrees
    CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
    self.layerView.layer.affineTransform = transform;
}

注意我們使用的旋轉(zhuǎn)常量是 M_PI_4 ,而不是你想象的45,因為iOS的變換函數(shù)使 用弧度而不是角度作為單位。弧度用數(shù)學(xué)常量pi的倍數(shù)表示,一個pi代表180度,所以四分之一的pi就是45度。

C的數(shù)學(xué)函數(shù)庫(iOS會自動引入)提供了pi的一些簡便的換算, M_PI_4于是就 是pi的四分之一,如果對換算不太清楚的話,可以用如下的宏做換算:

 #define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)

Core Graphics提供了一系列的函數(shù)可以在一個變換的基礎(chǔ)上做更深層次的變換, 如果做一個既要縮放又要旋轉(zhuǎn)的變換,這就會非常有用了。例如下面幾個函數(shù):

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

當(dāng)操縱一個變換的時候,初始生成一個什么都不做的變換很重要--也就是創(chuàng)建一 個 CGAffineTransform類型的空值,矩陣論中稱作單位矩陣,Core Graphics同 樣也提供了一個方便的常量:CGAffineTransformIdentity

最后,如果需要混合兩個已經(jīng)存在的變換矩陣,就可以使用如下方法,在兩個變換
的基礎(chǔ)上創(chuàng)建一個新的變換:

 CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2)

Demo
我們來用這些函數(shù)組合一個更加復(fù)雜的變換,先縮小50%,再旋轉(zhuǎn)30度,最后向右移動200個像素


- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a new transform
    CGAffineTransform transform = CGAffineTransformIdentity;
    //scale by 50%
    transform = CGAffineTransformScale(transform, 0.5, 0.5);
    //rotate by 30 degrees
    transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30);
    //translate by 200 points
    transform = CGAffineTransformTranslate(transform, 200, 0);
    //apply transform to layer
    self.layerView.layer.affineTransform = transform;
}

CATransfrom3D --3D變換

CG的前綴告訴我們, CGAffineTransform類型屬于Core Graphics框架,Core Graphics實際上是一個嚴格意義上的2D繪圖API,并且 CGAffineTransform 僅僅 對2D變換有效。

我們提到了zPosition 屬性,可以用來讓圖層靠近或者遠離相機 (用戶視角),transform屬性( CATransfrom3D類型)可以真正做到這點,即讓圖層在3D空間內(nèi)移動或者旋轉(zhuǎn)。

CGAffineTransform類似, CATransform3D 也是一個矩陣,但是和2x3的矩 陣不同,CATransform3D是一個可以在3維空間內(nèi)做變換的4x4的矩陣。

image.png

CGAffineTransform矩陣類似,Core Animation提供了一系列的方法用來創(chuàng)建和組合CATransform3D類型的矩陣, 和Core Graphics的函數(shù)類似, 但是3D的平移和旋轉(zhuǎn)多出了一個z參數(shù),并且旋轉(zhuǎn)函數(shù)除了angle之外多出了x,y,z三個參數(shù),分別決定了每個坐標方向上的旋轉(zhuǎn):

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

你應(yīng)該對X軸和Y軸比較熟悉了,分別以右和下為正方向,Z軸和這兩個軸分別垂直,指向視角外為正方向。

Demo
使用代碼利用CATransform3DMakeRotation對視圖內(nèi)的圖層 繞Y軸做了45度角的旋轉(zhuǎn),我們可以把視圖向右傾斜,這樣會看得更清晰。

- (void)viewDidLoad
{
    [super viewDidLoad];
    //rotate the layer 45 degrees along the Y axis
    CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0,1,0);
    self.layerView.layer.transform = transform;
}

角度

    /*
     旋轉(zhuǎn)的角度,后面三個是xyz,圍繞哪個軸旋轉(zhuǎn)就把哪個設(shè)置為1
     */
    CATransform3DMakeRotation(<#CGFloat angle#>, <#CGFloat x#>, <#CGFloat y#>, <#CGFloat z#>);

縮放

/*
 縮放,在沒個軸上縮放多少,都給你給一個值
 */
//    CATransform3DMakeScale(<#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)

平移

/*
     平移,沿著xyz各平移多少
    */
//    CATransform3DMakeTranslation(<#CGFloat tx#>, <#CGFloat ty#>, <#CGFloat tz#>)

具體代碼

/*這樣設(shè)置對z旋轉(zhuǎn)有效,因為旋轉(zhuǎn)z軸后,圖層依然是正對著用戶的,而x、y軸則是需要立體才能看到。對x、y軸進行旋轉(zhuǎn),因為需要3d效果才能看到,只是下面這樣設(shè)置是不行的,需要按照下下面方式設(shè)置成透視投影才能看出來*/
    CATransform3D transform3D = CATransform3DMakeRotation(M_PI_4, 1, 0, 0);
    self.imageView.layer.transform = transform3D;

    /*
     投影方式:1、正投影,2、透視投影
     */
    /*
     當(dāng)我們針對平面圖形進行旋轉(zhuǎn)時候,我們要修改他的透視投影
     */
    //     CATransform3DIdentity是單元矩陣的意思
    CATransform3D transform3D1 = CATransform3DIdentity;
    //設(shè)置單元矩陣中m34的值,變成透視投影
    transform3D1.m34 = -1.0/500;
    /*
     矩陣
     弧度
     圍繞哪個軸旋轉(zhuǎn)就給哪個參數(shù)1
     */
    transform3D1 = CATransform3DRotate(transform3D1, M_PI, 0, 1, 0);
//    self.imageView.layer.transform = transform3D1;

    //設(shè)置多個子圖層都做仿射變換
    self.subView.layer.sublayerTransform = transform3D1;

    //當(dāng)我們旋轉(zhuǎn)180度時候,也能展示照片,這是因為蘋果默認是開啟正背面渲染的,也就是正面背面都能看到,但是這樣很耗性能,因為用戶只能看到一個面,看不到那一面沒必要渲染取消正面背面渲染的話
    self.imageView.layer.doubleSided = NO;//關(guān)閉正背面渲染

CATransform3D

當(dāng)父圖層向外轉(zhuǎn),子圖層向里轉(zhuǎn)的時候,展示需要3D效果,但是手機本身是平面的,無法展現(xiàn)出3D效果,這個時候需要專用3D圖層來提供立體空間來展示

正方體的展示

- (void)viewDidLoad {
    [super viewDidLoad];

    self.containerView = [[UIView alloc]init];
    self.containerView.frame = CGRectMake(30, 100, 300, 300);
    self.containerView.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:self.containerView];

    self.faces = @[self.view1,self.view2,self.view3,self.view4,self.view5,self.view6];

    //父視圖的layer圖層
    CATransform3D perspective = CATransform3DIdentity;
    //設(shè)置為透視視圖
    perspective.m34 = -1/500;
    //延x軸旋轉(zhuǎn)45度
    perspective = CATransform3DRotate(perspective, M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, M_PI_4, 0, 1, 0);

    self.containerView.layer.sublayerTransform = perspective;

    CGFloat juli = 100;
    //添加第一個視圖,讓第一個視圖沿z軸平移100
    CATransform3D transform1 = CATransform3DMakeTranslation(0, 0, juli);
    [self addFace:self.faces[0] transform:transform1];
    //添加第二個面,研x軸平移100,并且研Y軸旋轉(zhuǎn)90度,使用make可以讓transform更新,而不使用make的api會讓效果疊加
    transform1 = CATransform3DMakeTranslation(juli, 0, 0);
    transform1 = CATransform3DRotate(transform1, M_PI_2, 0, 1, 0);
    [self addFace:self.faces[1] transform:transform1];

    //第三個面
    transform1 = CATransform3DMakeTranslation(0, -100, 0);
    transform1 = CATransform3DRotate(transform1, M_PI_2, 1, 0, 0);
    [self addFace:self.faces[2] transform:transform1];

    //第四個面
    transform1 = CATransform3DMakeTranslation(0, 100, 0);
    transform1 = CATransform3DRotate(transform1, -M_PI_2, 1, 0, 0);
    [self addFace:self.faces[3] transform:transform1];

    //第五個面
    transform1 = CATransform3DMakeTranslation(-100, 0,0);
    transform1 = CATransform3DRotate(transform1, -M_PI_2, 0, 1, 0);
    [self addFace:self.faces[4] transform:transform1];

    //第六個面
    transform1 = CATransform3DMakeTranslation(0, 0, -100);
    transform1 = CATransform3DRotate(transform1, M_PI, 0, 1, 0);
    [self addFace:self.faces[5] transform:transform1];

}



//將正方體的六個面都添加盡量,并做相應(yīng)的旋轉(zhuǎn)
-(void)addFace:(UIView *)view transform:(CATransform3D)transform{

    [self.containerView addSubview:view];

    CGSize containerSize = self.view.frame.size;
    view.center = CGPointMake(containerSize.width/2, containerSize.height/2);
    view.layer.transform = transform;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,958評論 2 373