iOS學(xué)習(xí)筆記(7)-動(dòng)畫基礎(chǔ)

iOS動(dòng)畫涉及圖形學(xué)的一些內(nèi)容汹族,已經(jīng)忘記的差不多了萧求,關(guān)于動(dòng)畫的筆記準(zhǔn)備分兩篇,第一篇總結(jié)動(dòng)畫基礎(chǔ)顶瞒,第二篇?jiǎng)t是完成一個(gè)旋轉(zhuǎn)動(dòng)畫的實(shí)例夸政。初學(xué)iOS,沒(méi)有太多經(jīng)驗(yàn)榴徐,總結(jié)的若有錯(cuò)漏守问,請(qǐng)各位指正。

1 圖層和視圖

在學(xué)習(xí)動(dòng)畫之前坑资,需要先明確幾個(gè)基本概念耗帕,首先是圖層和視圖袱贮。視圖是比較熟悉的了仿便,最初學(xué)習(xí)的時(shí)候就會(huì)見(jiàn)到有UIViewController,然后控制器會(huì)對(duì)應(yīng)一個(gè)UIView攒巍,這個(gè)UIView就是視圖嗽仪。我們知道視圖是有層級(jí)關(guān)系的,從UIWindow->UIView->SubView等柒莉。而之前學(xué)習(xí)中一直沒(méi)有深究的是闻坚,其實(shí)每個(gè)UIView都有一個(gè)CALayer實(shí)例的圖層屬性layer。視圖(UIView)的職責(zé)就是創(chuàng)建和管理圖層(CALayer)兢孝,視圖是對(duì)圖層的封裝窿凤,真正在iPhone屏幕上面顯示和做動(dòng)畫的其實(shí)都是視圖所關(guān)聯(lián)的圖層搀擂。視圖和圖層的關(guān)系是一一對(duì)應(yīng)的,如圖1所示為圖層樹結(jié)構(gòu)卷玉,Window Layer, View Layer等分別對(duì)應(yīng)視圖中的UIWindow哨颂,UIView等。

圖1 圖層樹結(jié)構(gòu)圖

最初看到這里也很疑惑相种,為什么要多出來(lái)一層封裝呢威恼?看了參考資料1才知道是為了提高復(fù)用性,因?yàn)樘O果公司除了iOS還有macOS寝并,一個(gè)適用于iPhone箫措,一個(gè)用于Mac,Mac基于鼠標(biāo)和觸控板和iPhone基于多點(diǎn)觸控的交互很不相同衬潦,因此iPhone里面是UIView斤蔓,Mac里面則是NSView,它們功能類似镀岛,但是實(shí)現(xiàn)并不同弦牡。可是對(duì)于繪圖漂羊,布局以及動(dòng)畫等兩個(gè)系統(tǒng)其實(shí)有很多可以共用的地方驾锰,因此獨(dú)立出一個(gè)Core Animation框架(CALayer中的CA就是Core Animation的縮寫)用于復(fù)用。當(dāng)然除了視圖層級(jí)和圖層樹這兩個(gè)層級(jí)走越,還有呈現(xiàn)樹和渲染樹椭豫,一共是四個(gè),在動(dòng)畫執(zhí)行過(guò)程中我們要獲取圖層屬性的話要使用呈現(xiàn)樹presentationLayer旨指,因?yàn)槲覀兊膱D層樹總是指向動(dòng)畫結(jié)束的最終位置赏酥,無(wú)法捕獲動(dòng)畫執(zhí)行過(guò)程中的屬性值。圖2為視圖谆构、圖層樹裸扶、呈現(xiàn)樹以及渲染樹的示意圖。

圖2 圖層樹低淡、呈現(xiàn)樹以及渲染樹

那么CALayer不能做什么姓言,能做什么呢瞬项?下面總結(jié)一下:

CALayer不能做什么

  • 既然是個(gè)獨(dú)立出來(lái)可復(fù)用的庫(kù)蔗蹋,那么CALayer是不能響應(yīng)和處理觸控事件的。

CALayer能做什么

  • 圖形陰影囱淋,邊框猪杭,圓角等。
  • 仿射變換妥衣。
  • 3D變換皂吮。
  • 透明遮罩戒傻,多級(jí)非線性動(dòng)畫...

那么既然CALayer可以做這些事情,我們寫個(gè)demo測(cè)試一下蜂筹,在一個(gè)黃色的UIView對(duì)應(yīng)的圖層CALayer上面添加一個(gè)藍(lán)色背景的子圖層需纳。

//CALayerDemo1-測(cè)試添加子圖層
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *layerView;
@property (strong, nonatomic) CALayer *blueLayer;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.blueLayer = [CALayer layer];
    self.blueLayer.frame = CGRectMake(25.0f, 25.0f, 50.0f, 50.0f);
    self.blueLayer.backgroundColor = [UIColor blueColor].CGColor;
    [self.layerView.layer addSublayer:self.blueLayer];
}
@end

注意到外面的黃色UIView的origin為{50,50},大小為200*200,可以發(fā)現(xiàn)子圖層的frame坐標(biāo)是相對(duì)于其父圖層坐標(biāo)系而言的(用addSubview添加子視圖的坐標(biāo)也是一樣)艺挪,運(yùn)行效果如下:

圖1 添加子圖層

當(dāng)然還可以設(shè)置CALayer的contents屬性為一個(gè)Image來(lái)設(shè)置圖片不翩,設(shè)置contentGravity來(lái)指定圖層內(nèi)容的拉伸方式。更多屬性可以參見(jiàn):iOS核心動(dòng)畫部分章節(jié)

2 坐標(biāo)系

2.1視圖坐標(biāo)系和圖層坐標(biāo)系

關(guān)于視圖的坐標(biāo)系麻裳,我在學(xué)習(xí)筆記一里面已經(jīng)總結(jié)過(guò)口蝠,這里順便一起看看視圖和圖層的坐標(biāo)系〗蚩樱可以發(fā)現(xiàn)與視圖相比妙蔗,在圖層中也有frame与帆,bounds夹孔,不同的是,圖層沒(méi)有視圖中的center猎提,而是多了個(gè)position穆役。當(dāng)然我們可以發(fā)現(xiàn)禁漓,這兩個(gè)值是一樣的。這里我們看到的只是二維的坐標(biāo)系孵睬,在后面我們會(huì)看到三維的坐標(biāo)系播歼。

圖2 視圖和圖層的坐標(biāo)系

2.2 錨點(diǎn)

center和position都指定了錨點(diǎn)(anchorPoint)相對(duì)于父圖層坐標(biāo)空間的位置,圖層的錨點(diǎn)通過(guò)position來(lái)控制圖層的位置掰读,可以把錨點(diǎn)認(rèn)為是移動(dòng)圖層的一個(gè)把柄秘狞。

圖3 錨點(diǎn)

如圖3為錨點(diǎn)的示意圖,錨點(diǎn)用單位坐標(biāo)來(lái)表示蹈集,圖層左上角為{0,0},中心為{0.5,0.5}烁试,這也是默認(rèn)值,右下角為{1,1}拢肆。右圖中將錨點(diǎn)設(shè)置到了{(lán)0,0}减响,可以發(fā)現(xiàn)圖層位置向右下發(fā)生了移動(dòng),注意郭怪,圖層frame的值發(fā)生了變化支示,但是position的值并沒(méi)有變化。這里可以用之前的例子來(lái)繼續(xù)測(cè)試一下鄙才,加入如下代碼在視圖要出現(xiàn)的時(shí)候修改錨點(diǎn)的位置颂鸿,可以發(fā)現(xiàn)打印出來(lái)的結(jié)果是符合我們預(yù)期的。

- (void)viewDidLoad {
    ......
    NSLog(@"frame:%@, sublayer frame:%@, position:%@", NSStringFromCGRect(self.layerView.frame), NSStringFromCGRect(self.blueLayer.frame), NSStringFromCGPoint(self.layerView.layer.position));
    //output: frame:{{50, 50}, {100, 100}}, sublayer frame:{{25, 25}, {50, 50}}, position:{100, 100}

}
- (void)viewWillAppear:(BOOL)animated {
    self.layerView.layer.anchorPoint = CGPointMake(0.0, 0.0);
    NSLog(@"frame:%@, sublayer frame:%@, position:%@", NSStringFromCGRect(self.layerView.frame), NSStringFromCGRect(self.blueLayer.frame), NSStringFromCGPoint(self.layerView.layer.position));
    //output: frame:{{100, 100}, {100, 100}}, sublayer frame:{{25, 25}, {50, 50}}, position:{100, 100}
}

這里可能會(huì)有個(gè)疑惑攒庵,就是根據(jù)錨點(diǎn)如何計(jì)算frame的位置嘴纺,計(jì)算公式如下败晴,由于position是錨點(diǎn)在superLayer的位置坐標(biāo),是保持不變的栽渴,通過(guò)修改錨點(diǎn)的值可以導(dǎo)致圖層的frame.origin發(fā)生變化尖坤,從而導(dǎo)致圖層位置發(fā)生變化:

frame.origin.x = position.x - anchorPoint.x * bounds.size.width; 
frame.origin.y = position.y - anchorPoint.y * bounds.size.height闲擦;

默認(rèn)情況下糖驴,錨點(diǎn)為{0.5,0.5},因此position正好位于圖層中心佛致。當(dāng)錨點(diǎn)改成{0,0}時(shí)贮缕,則此時(shí)由于position不變,可以看到我們上面例子的frame的origin變成了postion的值俺榆,也就是{100,100}感昼,運(yùn)行效果如圖4所示。錨點(diǎn)的用法有個(gè)很經(jīng)典的鬧鐘例子罐脊,參見(jiàn)這篇文章定嗓。

圖4 設(shè)置錨點(diǎn)為{0,0}

2.3 三維坐標(biāo)系

據(jù)說(shuō)平面直角坐標(biāo)系是笛卡爾在一次生病的時(shí)候發(fā)明的,而三維坐標(biāo)系是后人在二維坐標(biāo)系基礎(chǔ)上發(fā)展而來(lái)萍桌。三維坐標(biāo)系通常分為兩種:左手坐標(biāo)系和右手坐標(biāo)系宵溅。iOS用的是左手坐標(biāo)系(Mac用的是右手坐標(biāo)系,我們這里不討論)上炎∈崖撸可以通過(guò)左手定則(圖6)來(lái)判斷旋轉(zhuǎn)的方向:使用左手握住拳頭,拇指指向旋轉(zhuǎn)軸的正方向藕施,四指彎曲方向就是旋轉(zhuǎn)的正方向寇损。

圖5 三維坐標(biāo)系
圖6 左手定則

我們知道iOS坐標(biāo)中,原點(diǎn)位于左上角裳食,X軸向右矛市,Y軸向下為正方向。圖7給出了iOS中三維坐標(biāo)中各個(gè)軸旋轉(zhuǎn)方向的示意圖诲祸,通過(guò)左手定則比劃一下應(yīng)該就清楚了浊吏。

圖7 iOS三個(gè)軸的旋轉(zhuǎn)方向示意圖

3 變換

在iOS的動(dòng)畫效果中,變換是很常見(jiàn)的救氯,包括仿射變換和3D變換等找田。變換的終極原理就是矩陣的乘法運(yùn)算,到這個(gè)時(shí)候終于發(fā)現(xiàn)以前本科學(xué)習(xí)矩陣的用處了径密。

3.1 仿射變換

通過(guò)設(shè)置UIView的transform屬性可以實(shí)現(xiàn)圖層的二維旋轉(zhuǎn)午阵,縮放以及平移躺孝,這一系列的變換歸類為仿射變換享扔,如圖8所示就是多次復(fù)合變換底桂,包括了旋轉(zhuǎn),縮放惧眠,平移籽懦。

圖8 仿射變換實(shí)例

UIView的transform是一個(gè)CGAffineTransform類型的實(shí)例,CGAffineTransform是一個(gè)可以和二維空間向量(如CGPoint)做乘法的3X3的矩陣氛魁。矩陣乘法如下:

圖9 仿射變換矩陣

注意到我們對(duì)CGPoint增加一列暮顺,對(duì)變換矩陣也增加了[0 0 1]那個(gè)第三列,多增加的一列主要是為了復(fù)合變換中的矩陣相乘秀存,試想捶码,如果我們不加第三列,那么兩個(gè) 3*2的矩陣是不能相乘的或链。由上面的矩陣計(jì)算可以得到變換后的坐標(biāo)值惫恼,如下:

圖10 矩陣計(jì)算

因此我們可以發(fā)現(xiàn),當(dāng)變換矩陣為圖11這樣時(shí)澳盐,可以得到新的坐標(biāo)值如圖12所示祈纯,即完成了一次平移操作。

圖11 平移的變換矩陣
圖12 平移的坐標(biāo)計(jì)算

而當(dāng)變換矩陣為圖13這樣時(shí)叼耙,則可以完成一次縮放操作腕窥,注意縮放的時(shí)候center保持不變

圖13 縮放矩陣
圖14 縮放的坐標(biāo)計(jì)算

同理筛婉,要完成旋轉(zhuǎn)簇爆,則旋轉(zhuǎn)的變換矩陣如下,相比前面的顯而易見(jiàn)爽撒,旋轉(zhuǎn)的稍微復(fù)雜一點(diǎn)冕碟,不過(guò)你可以畫一個(gè)單位圓,然后通過(guò)旋轉(zhuǎn)一個(gè)角度a匆浙,然后運(yùn)用下正弦和余弦的幾個(gè)定理就可以得到這個(gè)公式了安寺。

圖15 旋轉(zhuǎn)矩陣
圖16 旋轉(zhuǎn)的坐標(biāo)計(jì)算

同樣的,還是用之前的那個(gè)實(shí)例首尼,即把layerView先縮放挑庶,再旋轉(zhuǎn)然后平移,viewDidLoad中增加代碼如下软能,運(yùn)行效果如圖17所示迎捺。

    ......
   //創(chuàng)建transform對(duì)象
    CGAffineTransform transform = CGAffineTransformIdentity; 
    //縮放為原來(lái)大小的50%
    transform = CGAffineTransformScale(transform, 0.5, 0.5);
    //旋轉(zhuǎn)30度
    transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0);
    //X方向平移200
    transform = CGAffineTransformTranslate(transform, 200, 0);
    //設(shè)置transform
    self.layerView.layer.affineTransform = transform;
圖17 復(fù)合變換效果圖

3.2 3D變換

在iOS中使用CATransform3D這個(gè)結(jié)構(gòu)體來(lái)表示三維的齊次坐標(biāo)變換矩陣。3D變換涉及到三維透視投影的一些原理知識(shí)查排,具體原理可以參見(jiàn)圖形學(xué)的相關(guān)書籍凳枝,這里只是給出iOS里面的3D變換用法以及基本的結(jié)論。關(guān)于三維透視投影的一些介紹可以參見(jiàn)參考資料3,4岖瑰。

CATransform3D結(jié)構(gòu)體在iOS中的定義如下:

struct CATransform3D{ 
    CGFloat m11, m12, m13, m14; 
    CGFloat m21, m22, m23, m24; 
    CGFloat m31, m32, m33, m34; 
    CGFloat m41, m42, m43, m44;
};

iOS的3D變換用的變換矩陣如下所示叛买,注意到坐標(biāo)是1X4的矩陣,而變換矩陣是4X4的矩陣蹋订,這里面的m34這個(gè)值是用來(lái)設(shè)置透視效果的率挣。我們可以通過(guò)設(shè)置m34為-1.0 / d來(lái)應(yīng)用透視效果,d代表了想象中視角相機(jī)和屏幕之間的距離露戒,以像素為單位椒功。通過(guò)設(shè)置d的值可以達(dá)到近大遠(yuǎn)小的效果,也就是我們看到在iOS開發(fā)中以坐標(biāo)軸旋轉(zhuǎn)圖層時(shí)智什,產(chǎn)生的3D效果动漾。d越大,效果越不明顯荠锭,d越小谦炬,效果越明顯甚至導(dǎo)致失真。d的一個(gè)推薦的值是500-1000之間节沦。

圖18 一個(gè)3D像素點(diǎn)CATransform3D矩陣變換

在例子里面加上3D旋轉(zhuǎn)的代碼如下键思,這里是沿Y軸旋轉(zhuǎn)45度:

    ......
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = - 1.0 / 500.0;
    transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
    self.layerView.layer.transform = transform;

如圖19就是沿Y軸旋轉(zhuǎn)45度的得到的效果圖。這里設(shè)置的d為500甫贯,我們可以發(fā)現(xiàn)3D效果還算明顯且沒(méi)有很夸張吼鳞。旋轉(zhuǎn)45度,靠近我們的邊會(huì)變大而遠(yuǎn)離的邊會(huì)縮小叫搁,這樣從視覺(jué)上產(chǎn)生了3D效果赔桌。如果我們?cè)O(shè)置d為10,這樣會(huì)發(fā)現(xiàn)3D效果會(huì)夸張到失真渴逻。而如果設(shè)置d為1000000會(huì)更大的值疾党,會(huì)發(fā)現(xiàn)3D效果很不明顯,iOS默認(rèn)設(shè)置的d就是無(wú)窮大惨奕,因此如果不設(shè)置m34的值雪位,我們旋轉(zhuǎn)是沒(méi)有3D效果的。

圖19 3D變換效果圖 d=500
圖20 3D變換效果圖 d=50

4 總結(jié)

iOS動(dòng)畫開發(fā)涉及內(nèi)容很多梨撞,這里只是摘取了一些我目前了解的基礎(chǔ)知識(shí)雹洗,后面會(huì)寫一篇筆記來(lái)做一個(gè)動(dòng)畫的實(shí)例。對(duì)于3D透視投影這一塊的理論沒(méi)有細(xì)究卧波,希望后面會(huì)有時(shí)間研究清楚并補(bǔ)充了时肿。

5 參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末港粱,一起剝皮案震驚了整個(gè)濱河市螃成,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖寸宏,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宁炫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡击吱,警方通過(guò)查閱死者的電腦和手機(jī)淋淀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門遥昧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)覆醇,“玉大人,你說(shuō)我怎么就攤上這事炭臭∮琅В” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵鞋仍,是天一觀的道長(zhǎng)常摧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)威创,這世上最難降的妖魔是什么落午? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮肚豺,結(jié)果婚禮上溃斋,老公的妹妹穿的比我還像新娘。我一直安慰自己吸申,他們只是感情好梗劫,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著截碴,像睡著了一般梳侨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上日丹,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天走哺,我揣著相機(jī)與錄音,去河邊找鬼哲虾。 笑死割坠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妒牙。 我是一名探鬼主播彼哼,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼湘今!你這毒婦竟也來(lái)了敢朱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拴签,沒(méi)想到半個(gè)月后孝常,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚓哩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年构灸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岸梨。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喜颁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出曹阔,到底是詐尸還是另有隱情半开,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布赃份,位于F島的核電站寂拆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏抓韩。R本人自食惡果不足惜纠永,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谒拴。 院中可真熱鬧尝江,春花似錦、人聲如沸彪薛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)善延。三九已至少态,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間易遣,已是汗流浹背彼妻。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豆茫,地道東北人侨歉。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像揩魂,于是被迫代替她去往敵國(guó)和親幽邓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容