W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
CG的前綴告訴我們,CGAffineTransform
類型屬于Core Graphics框架,Core Graphics實際上是一個嚴格意義上的2D繪圖API,并且CGAffineTransform
僅僅對2D變換有效。
在第三章中,我們提到了zPosition
屬性,可以用來讓圖層靠近或者遠離相機(用戶視角),transform
屬性(CATransform3D
類型)可以真正做到這點,即讓圖層在3D空間內(nèi)移動或者旋轉(zhuǎn)。
和CGAffineTransform
類似,CATransform3D
也是一個矩陣,但是和2x3的矩陣不同,CATransform3D
是一個可以在3維空間內(nèi)做變換的4x4的矩陣(圖5.6)。
圖5.7 X,Y,Z軸,以及圍繞它們旋轉(zhuǎn)的方向
由圖所見,繞Z軸的旋轉(zhuǎn)等同于之前二維空間的仿射旋轉(zhuǎn),但是繞X軸和Y軸的旋轉(zhuǎn)就突破了屏幕的二維空間,并且在用戶視角看來發(fā)生了傾斜。
舉個例子:清單5.4的代碼使用了CATransform3DMakeRotation
對視圖內(nèi)的圖層繞Y軸做了45度角的旋轉(zhuǎn),我們可以把視圖向右傾斜,這樣會看得更清晰。
結(jié)果見圖5.8,但并不像我們期待的那樣。
清單5.4 繞Y軸旋轉(zhuǎn)圖層
@implementation ViewController
- (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;
}
@end
圖5.9?CATransform3D
的m34
元素,用來做透視
m34
的默認值是0,我們可以通過設(shè)置m34
為-1.0 /?d
來應(yīng)用透視效果,d
代表了想象中視角相機和屏幕之間的距離,以像素為單位,那應(yīng)該如何計算這個距離呢?實際上并不需要,大概估算一個就好了。
因為視角相機實際上并不存在,所以可以根據(jù)屏幕上的顯示效果自由決定它的防止的位置。通常500-1000就已經(jīng)很好了,但對于特定的圖層有時候更小后者更大的值會看起來更舒服,減少距離的值會增強透視效果,所以一個非常微小的值會讓它看起來更加失真,然而一個非常大的值會讓它基本失去透視效果,對視圖應(yīng)用透視的代碼見清單5.5,結(jié)果見圖5.10。
清單5.5 對變換應(yīng)用透視效果
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//create a new transform
CATransform3D transform = CATransform3DIdentity;
//apply perspective
transform.m34 = - 1.0 / 500.0;
//rotate by 45 degrees along the Y axis
transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
//apply to layer
self.layerView.layer.transform = transform;
}
@end
圖5.11 滅點
Core Animation定義了這個點位于變換圖層的anchorPoint
(通常位于圖層中心,但也有例外,見第三章)。這就是說,當(dāng)圖層發(fā)生變換時,這個點永遠位于圖層變換之前anchorPoint
的位置。
當(dāng)改變一個圖層的position
,你也改變了它的滅點,做3D變換的時候要時刻記住這一點,當(dāng)你視圖通過調(diào)整m34
來讓它更加有3D效果,應(yīng)該首先把它放置于屏幕中央,然后通過平移來把它移動到指定位置(而不是直接改變它的position
),這樣所有的3D圖層都共享一個滅點。
sublayerTransform
屬性如果有多個視圖或者圖層,每個都做3D變換,那就需要分別設(shè)置相同的m34值,并且確保在變換之前都在屏幕中央共享同一個position
,如果用一個函數(shù)封裝這些操作的確會更加方便,但仍然有限制(例如,你不能在Interface Builder中擺放視圖),這里有一個更好的方法。
CALayer
有一個屬性叫做sublayerTransform
。它也是CATransform3D
類型,但和對一個圖層的變換不同,它影響到所有的子圖層。這意味著你可以一次性對包含這些圖層的容器做變換,于是所有的子圖層都自動繼承了這個變換方法。
相較而言,通過在一個地方設(shè)置透視變換會很方便,同時它會帶來另一個顯著的優(yōu)勢:滅點被設(shè)置在容器圖層的中點,從而不需要再對子圖層分別設(shè)置了。這意味著你可以隨意使用position
和frame
來放置子圖層,而不需要把它們放置在屏幕中點,然后為了保證統(tǒng)一的滅點用變換來做平移。
我們來用一個demo舉例說明。這里用Interface Builder并排放置兩個視圖(圖5.12),然后通過設(shè)置它們?nèi)萜饕晥D的透視變換,我們可以保證它們有相同的透視和滅點,代碼見清單5.6,結(jié)果見圖5.13。
圖5.13 通過相同的透視效果分別對視圖做變換
我們既然可以在3D場景下旋轉(zhuǎn)圖層,那么也可以從背面去觀察它。如果我們在清單5.4中把角度修改為M_PI
(180度)而不是當(dāng)前的M_PI_4
(45度),那么將會把圖層完全旋轉(zhuǎn)一個半圈,于是完全背對了相機視角。
那么從背部看圖層是什么樣的呢,見圖5.14
圖5.15 反方向變換的嵌套圖層
注意做了-45度旋轉(zhuǎn)的內(nèi)部圖層是怎樣抵消旋轉(zhuǎn)45度的圖層,從而恢復(fù)正常狀態(tài)的。
如果內(nèi)部圖層相對外部圖層做了相反的變換(這里是繞Z軸的旋轉(zhuǎn)),那么按照邏輯這兩個變換將被相互抵消。
驗證一下,相應(yīng)代碼見清單5.7,結(jié)果見5.16
清單5.7 繞Z軸做相反的旋轉(zhuǎn)變換
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *outerView;
@property (nonatomic, weak) IBOutlet UIView *innerView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//rotate the outer layer 45 degrees
CATransform3D outer = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
self.outerView.layer.transform = outer;
//rotate the inner layer -45 degrees
CATransform3D inner = CATransform3DMakeRotation(-M_PI_4, 0, 0, 1);
self.innerView.layer.transform = inner;
}
@end
圖5.17 繞Y軸做相反旋轉(zhuǎn)的預(yù)期結(jié)果。
但其實這并不是我們所看到的,相反,我們看到的結(jié)果如圖5.18所示。發(fā)什么了什么呢?內(nèi)部的圖層仍然向左側(cè)旋轉(zhuǎn),并且發(fā)生了扭曲,但按道理說它應(yīng)該保持正面朝上,并且顯示正常的方塊。
這是由于盡管Core Animation圖層存在于3D空間之內(nèi),但它們并不都存在同一個3D空間。每個圖層的3D場景其實是扁平化的,當(dāng)你從正面觀察一個圖層,看到的實際上由子圖層創(chuàng)建的想象出來的3D場景,但當(dāng)你傾斜這個圖層,你會發(fā)現(xiàn)實際上這個3D場景僅僅是被繪制在圖層的表面。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: