iOS動(dòng)畫-Transform和KeyFrame動(dòng)畫

序言

追求美好是人的天性犁功,這是猿們無(wú)法避免的囚霸。我們總是追求更為酷炫的實(shí)現(xiàn)瞧省,如果足夠仔細(xì)硼一,我們不難發(fā)現(xiàn)一個(gè)好的動(dòng)畫通過(guò)步驟分解后本質(zhì)上不過(guò)是一個(gè)個(gè)簡(jiǎn)單的動(dòng)畫實(shí)現(xiàn)累澡,正是這些基本的動(dòng)畫在經(jīng)過(guò)合理的搭配組合后化腐朽為神奇,令人驚艷欠动。因此永乌,掌握最基本的動(dòng)畫是完成酷炫開發(fā)之旅的根本。

作為動(dòng)畫篇的第二篇文章具伍,我在從UIView動(dòng)畫說(shuō)起簡(jiǎn)單介紹了關(guān)于UIView的幾種基本動(dòng)畫,這幾種動(dòng)畫的搭配讓我們的登錄界面富有靈性生動(dòng)圈驼,但是這幾種動(dòng)畫總是無(wú)法滿足我們對(duì)于動(dòng)畫的需求人芽。同樣的,本文將從一個(gè)小demo開始講解強(qiáng)大的transform動(dòng)畫以及關(guān)鍵幀keyFrame動(dòng)畫绩脆。

demo動(dòng)效圖

可以看到兩個(gè)動(dòng)畫:葉子被風(fēng)吹落以及左邊的文字從summer變化到autumn萤厅,這兩個(gè)動(dòng)畫都是基于強(qiáng)大的transform形變橄抹,其中葉子的飄落動(dòng)畫通過(guò)關(guān)鍵幀動(dòng)畫實(shí)現(xiàn)。demo鏈接

transform動(dòng)畫

transform是一個(gè)非常重要的屬性惕味,它在矩陣變換的層面上改變視圖的顯示效果楼誓,完成旋轉(zhuǎn)、形變名挥、平移等等操作疟羹。在它被修改的同時(shí),視圖的frame也會(huì)被真實(shí)改變禀倔。有兩個(gè)數(shù)據(jù)類型用來(lái)表示transform榄融,分別是CGAffineTransform和CATransform3D。前者作用于UIView救湖,后者為layer層次的變換類型愧杯。基于后者可以實(shí)現(xiàn)更加強(qiáng)大的功能鞋既,但我們需要先掌握CGAffineTransform類型的使用力九。同時(shí),本文講解也是這個(gè)變換類型邑闺。

對(duì)于想要了解矩陣變換是如何作用實(shí)現(xiàn)的畏邢,可以參考這篇博客:CGAffineTransform 放射變換

talk is cheap show you the code

在開始使用transform實(shí)現(xiàn)你的動(dòng)畫之前,我先介紹幾個(gè)常用的函數(shù):

/// 用來(lái)連接兩個(gè)變換效果并返回检吆。返回的t = t1 * t2CGAffineTransformConcat(CGAffineTransformt1,CGAffineTransformt2)/// 矩陣初始值舒萎。[ 1 0 0 1 0 0 ]CGAffineTransformIdentity/// 自定義矩陣變換,需要掌握矩陣變換的知識(shí)才知道怎么用蹭沛。參照上面推薦的原理鏈接CGAffineTransformMake(CGFloata,CGFloatb,CGFloatc,CGFloatd,CGFloattx,CGFloatty)/// 旋轉(zhuǎn)視圖臂寝。傳入?yún)?shù)為 角度 * (M_PI / 180)。等同于 CGAffineTransformRotate(self.transform, angle)CGAffineTransformMakeRotation(CGFloatangle)CGAffineTransformRotate(CGAffineTransformt,CGFloatangle)/// 縮放視圖摊灭。等同于CGAffineTransformScale(self.transform, sx, sy)CGAffineTransformMakeScale(CGFloatsx,CGFloatsy)CGAffineTransformScale(CGAffineTransformt,CGFloatsx,CGFloatsy)/// 縮放視圖咆贬。等同于CGAffineTransformTranslate(self.transform, tx, ty)CGAffineTransformMakeTranslation(CGFloattx,CGFloatty)CGAffineTransformTranslate(CGAffineTransformt,CGFloattx,CGFloatty)

我把demo左下角文字的變形過(guò)程記錄下來(lái)。這里推薦mac上面的一款截取動(dòng)圖的程序licecap帚呼,非常簡(jiǎn)單好用掏缎。博主用它來(lái)分解動(dòng)畫步驟,然后進(jìn)行重現(xiàn)煤杀。

文字變形過(guò)程

不難看出在文字的動(dòng)畫中做了兩個(gè)處理:y軸上的形變縮小眷蜈、透明度的漸變過(guò)程。首先在項(xiàng)目中新增兩個(gè)UILabel沈自,分別命名為label1酌儒、label2.然后在viewDidAppear中加入這么一段代碼:

- (void)viewDidAppear: (BOOL)animated {? ? label1.transform = CGAffineTransformMakeScale(1,0);? ? label1.alpha =0;? ? [UIView animateWithDuration:3.animations: ^ {? ? ? ? label1.transform = CGAffineTransformMakeScale(1,1);? ? ? ? label2.transform = CGAffineTransformMakeScale(1,0.1);? ? ? ? label1.alpha =1;? ? ? ? label2.alpha =0;? ? }];}

這里解釋一下為什么label2為什么在動(dòng)畫中y軸逐漸縮小為0.1而不是0。如果我們?cè)O(shè)為0的話枯途,那么在動(dòng)畫提交之后忌怎,label2會(huì)直接保持動(dòng)畫結(jié)束的狀態(tài)(這是出于性能優(yōu)化自動(dòng)完成的)籍滴,因此在使用任何縮小的形變時(shí),你可以將縮小值設(shè)置的很小榴啸,只要不是0孽惰。

運(yùn)行你的代碼,文字的形變過(guò)程你已經(jīng)做出來(lái)了鸥印,但是demo中的動(dòng)畫不僅僅是形變勋功,還包括位移的過(guò)程。很顯然辅甥,我們可以通過(guò)改變center的位置來(lái)實(shí)現(xiàn)這個(gè)效果酝润,但這顯然不是我們今天想要的結(jié)果,實(shí)現(xiàn)新的動(dòng)畫方式來(lái)實(shí)現(xiàn)更有意義璃弄。

動(dòng)畫開始時(shí)形變出現(xiàn)的label高度為0要销,然后逐漸的的變高變?yōu)閔eight,而label從頭到尾基于頂部的位置不發(fā)生改變夏块。因此動(dòng)畫開始前這個(gè)label在y軸上的位置是0疏咐,在完成顯示之后的y軸中心點(diǎn)為height / 2(基于label自身的坐標(biāo)系而言),那么動(dòng)畫的代碼就可以寫成這樣:

- (void)viewDidAppear: (BOOL)animated {///? 初始化動(dòng)畫開始前l(fā)abel的位置CGFloat offset = label1.frame.size.height *0.5;? ? label1.transform = CGAffineTransformConcat(? ? ? CGAffineTransformMakeScale(0,0),? ? ? CGAffineTransformTranslate(0, -offset)? ? );? ? label1.alpha =0;? ? [UIView animateWithDuration:3.animations: ^ {///? 還原label1的變換狀態(tài)并形變和偏移label2label1.transform = CGAffineTransformIdentifier;? ? ? ? label1.transform = CGAffineTransformConcat(? ? ? ? ? CGAffineTransformMakeScale(0,0),? ? ? ? ? CGAffineTransformTranslate(0, offset)? ? ? ? );? ? ? ? label1.alpha =1;? ? ? ? label2.alpha =0;? ? }];}

調(diào)整兩個(gè)label的位置脐供,并且設(shè)置其中一個(gè)透明顯示浑塞。然后運(yùn)行這段代碼,你會(huì)發(fā)現(xiàn)文字轉(zhuǎn)變過(guò)程的動(dòng)畫完成了政己。

keyframe動(dòng)畫

將文章開頭的gif圖另存為到本地酌壕,然后使用預(yù)覽打開看看,你會(huì)發(fā)現(xiàn)預(yù)覽中的gif圖變成了很多張的圖片歇由。實(shí)際上卵牍,無(wú)論是動(dòng)畫、電影沦泌、CG等動(dòng)態(tài)效果糊昙,都可以看做是一張張圖片接連渲染實(shí)現(xiàn)的,而這些圖片切換的速度足夠快時(shí)我們就會(huì)當(dāng)做是動(dòng)畫谢谦。在此之前我們所講述的平移視圖在UIView動(dòng)畫提交之后系統(tǒng)會(huì)根據(jù)動(dòng)畫時(shí)長(zhǎng)計(jì)算出視圖移動(dòng)的所有幀界面释牺,然后逐個(gè)渲染。

回到我們demo中的落葉動(dòng)畫來(lái)回挽,我總共對(duì)葉子的center進(jìn)行過(guò)五次修改没咙,我將落葉平移的線性路徑繪制出來(lái)并且標(biāo)注關(guān)鍵的轉(zhuǎn)折點(diǎn):

1.png

上面這個(gè)平移用UIView動(dòng)畫代碼要如何實(shí)現(xiàn)呢?毫無(wú)疑問(wèn)厅各,我們需要不斷的嵌套UIView動(dòng)畫的使用來(lái)實(shí)現(xiàn)镜撩,具體代碼如下:

[selfmoveLeafWithOffset:(CGPoint){15,80} completion: ^(BOOLfinished){[selfmoveLeafWithOffset:(CGPoint){30,105} completion: ^(BOOLfinished){[selfmoveLeafWithOffset:(CGPoint){40,110} completion: ^(BOOLfinished){[selfmoveLeafWithOffset:(CGPoint){90,80} completion: ^(BOOLfinished){[selfmoveLeafWithOffset:(CGPoint){80,60} completion: nil duration:0.6];} duration:1.2];} duration:1.2];} duration:0.6];} duration:0.4];-(void)moveLeafWithOffset:(CGPoint)offset completion:(void(^)(BOOLfinished))completion duration:(NSTimeInterval)duration{[UIViewanimateWithDuration: duration delay:0options: UIViewAnimationOptionCurveLinear animations: ^{? ? ? ? CGPoint center = _leaf.center;center.x += offset.x;center.y += offset.y;_leaf.center = center;} completion: completion];}

看起來(lái)還蠻容易的,上面的代碼只是移動(dòng)葉子队塘,在gif圖中我們的葉子還有旋轉(zhuǎn)袁梗,因此我們還需要加上這么一段代碼:

[UIViewanimateWithDuration:4animations: ^{? ? _leaf.transform = CGAffineTransformMakeRotation(M_PI);}];

那么ok,運(yùn)行這段代碼看看憔古,落葉的移動(dòng)非常的生硬遮怜,我們可以明顯的看到拐角。其次鸿市,這段代碼中的duration傳入是沒(méi)有任何意義的(傳入一個(gè)固定的動(dòng)畫時(shí)長(zhǎng)無(wú)法體現(xiàn)出在落葉飄下這一過(guò)程中的層次步驟)

對(duì)于這兩個(gè)問(wèn)題锯梁,UIView也提供了另一種動(dòng)畫方式來(lái)幫助我們解決這兩個(gè)問(wèn)題 —— keyframe動(dòng)畫:

+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void(^)(void))animations completion:(void(^ __nullable)(BOOLfinished))completion+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void(^)(void))animations

第一個(gè)方法是創(chuàng)建一個(gè)關(guān)鍵幀動(dòng)畫,第二個(gè)方法用于在動(dòng)畫的代碼塊中插入關(guān)鍵幀動(dòng)畫信息焰情,兩個(gè)參數(shù)的意義表示如下:

frameStartTime 表示關(guān)鍵幀動(dòng)畫開始的時(shí)刻在整個(gè)動(dòng)畫中的百分比

frameDuration 表示這個(gè)關(guān)鍵幀動(dòng)畫占用整個(gè)動(dòng)畫時(shí)長(zhǎng)的百分比陌凳。

我做了一張圖片來(lái)表示參數(shù)含義:

添加關(guān)鍵幀方法參數(shù)說(shuō)明

對(duì)比UIView動(dòng)畫跟關(guān)鍵幀動(dòng)畫,關(guān)鍵幀動(dòng)畫引入了動(dòng)畫占比時(shí)長(zhǎng)的概念内舟,這讓我們能控制每個(gè)關(guān)鍵幀動(dòng)畫的占用比例而不是傳入一個(gè)無(wú)意義的動(dòng)畫時(shí)長(zhǎng) —— 這讓我們的代碼更加難以理解合敦。當(dāng)然,除了動(dòng)畫占比之外验游,關(guān)鍵幀動(dòng)畫的options參數(shù)也讓動(dòng)畫變得更加平滑充岛,下面是關(guān)鍵幀特有的配置參數(shù):

UIViewKeyframeAnimationOptionCalculationModeLinear// 連續(xù)運(yùn)算模式,線性UIViewKeyframeAnimationOptionCalculationModeDiscrete// 離散運(yùn)算模式耕蝉,只顯示關(guān)鍵幀UIViewKeyframeAnimationOptionCalculationModePaced// 均勻執(zhí)行運(yùn)算模式崔梗,線性UIViewKeyframeAnimationOptionCalculationModeCubic// 平滑運(yùn)算模式UIViewKeyframeAnimationOptionCalculationModeCubicPaced// 平滑均勻運(yùn)算模式

在demo中我使用的是UIViewKeyframeAnimationOptionCalculationModeCubic,這個(gè)參數(shù)使用了貝塞爾曲線讓落葉的下落動(dòng)畫變得更加平滑垒在。效果可見最開始的gif動(dòng)畫蒜魄,你可以修改demo傳入的不同參數(shù)來(lái)查看效果。接下來(lái)我們就根據(jù)新的方法把上面的UIView動(dòng)畫轉(zhuǎn)換成關(guān)鍵幀動(dòng)畫代碼场躯,具體代碼如下:

[UIView animateKeyframesWithDuration:4delay:0options: UIViewKeyframeAnimationOptionCalculationModeLinear animations: ^{? ? __block CGPoint center = _leaf.center;? ? [UIView addKeyframeWithRelativeStartTime:0relativeDuration:0.1animations: ^{? ? ? ? _leaf.center = (CGPoint){ center.x +15, center.y +80};? ? }];? ? [UIView addKeyframeWithRelativeStartTime:0.1relativeDuration:0.15animations: ^{? ? ? ? _leaf.center = (CGPoint){ center.x +45, center.y +185};? ? }];? ? [UIView addKeyframeWithRelativeStartTime:0.25relativeDuration:0.3animations: ^{? ? ? ? _leaf.center = (CGPoint){ center.x +90, center.y +295};? ? }];? ? [UIView addKeyframeWithRelativeStartTime:0.55relativeDuration:0.3animations: ^{? ? ? ? _leaf.center = (CGPoint){ center.x +180, center.y +375};? ? }];? ? [UIView addKeyframeWithRelativeStartTime:0.85relativeDuration:0.15animations: ^{? ? ? ? _leaf.center = (CGPoint){ center.x +260, center.y +435};? ? }];? ? [UIView addKeyframeWithRelativeStartTime:0relativeDuration:1animations: ^{? ? ? ? _leaf.transform = CGAffineTransformMakeRotation(M_PI);? ? }];} completion: nil];

可以看到相比UIView的動(dòng)畫谈为,關(guān)鍵幀動(dòng)畫更加直觀的讓我們明白每一次平移動(dòng)畫的時(shí)間占比,代碼也相對(duì)的更加簡(jiǎn)潔推盛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末峦阁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子耘成,更是在濱河造成了極大的恐慌榔昔,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘪菌,死亡現(xiàn)場(chǎng)離奇詭異撒会,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)师妙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門诵肛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人默穴,你說(shuō)我怎么就攤上這事怔檩⊥市悖” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵薛训,是天一觀的道長(zhǎng)媒吗。 經(jīng)常有香客問(wèn)我,道長(zhǎng)乙埃,這世上最難降的妖魔是什么闸英? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮介袜,結(jié)果婚禮上甫何,老公的妹妹穿的比我還像新娘。我一直安慰自己遇伞,他們只是感情好辙喂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赃额,像睡著了一般加派。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跳芳,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天芍锦,我揣著相機(jī)與錄音,去河邊找鬼飞盆。 笑死娄琉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吓歇。 我是一名探鬼主播孽水,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼城看!你這毒婦竟也來(lái)了女气?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤测柠,失蹤者是張志新(化名)和其女友劉穎炼鞠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轰胁,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谒主,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赃阀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霎肯。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出观游,到底是詐尸還是另有隱情搂捧,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布备典,位于F島的核電站异旧,受9級(jí)特大地震影響意述,放射性物質(zhì)發(fā)生泄漏提佣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一荤崇、第九天 我趴在偏房一處隱蔽的房頂上張望拌屏。 院中可真熱鬧,春花似錦术荤、人聲如沸倚喂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)端圈。三九已至,卻和暖如春子库,著一層夾襖步出監(jiān)牢的瞬間舱权,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工仑嗅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宴倍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓仓技,卻偏偏與公主長(zhǎng)得像鸵贬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子脖捻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 序言 追求美好是人的天性阔逼,這是猿們無(wú)法避免的。我們總是追求更為酷炫的實(shí)現(xiàn)地沮,如果足夠仔細(xì)嗜浮,我們不難發(fā)現(xiàn)一個(gè)好的動(dòng)畫通...
    sindri的小巢閱讀 16,716評(píng)論 31 97
  • Core Animation Core Animation,中文翻譯為核心動(dòng)畫诉濒,它是一組非常強(qiáng)大的動(dòng)畫處理API周伦,...
    45b645c5912e閱讀 3,015評(píng)論 0 21
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜未荒,今天將帶大家一窺iOS動(dòng)畫全貌专挪。在這里你可以看...
    F麥子閱讀 5,094評(píng)論 5 13
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌寨腔。在這里你可以看...
    每天刷兩次牙閱讀 8,465評(píng)論 6 30
  • 在iOS實(shí)際開發(fā)中常用的動(dòng)畫無(wú)非是以下四種:UIView動(dòng)畫速侈,核心動(dòng)畫,幀動(dòng)畫迫卢,自定義轉(zhuǎn)場(chǎng)動(dòng)畫倚搬。 1.UIView...
    請(qǐng)叫我周小帥閱讀 3,078評(píng)論 1 23