iOS 開發之動畫篇 - Transform和KeyFrame動畫

來源:互聯網
上載者:User

iOS 開發之動畫篇 - Transform和KeyFrame動畫
序言

追求美好是人的天性,這是猿們無法避免的。我們總是追求更為酷炫的實現,如果足夠仔細,我們不難發現一個好的動畫通過步驟分解後本質上不過是一個個簡單的動畫實現,正是這些基本的動畫在經過合理的搭配組合後化腐朽為神奇,令人驚豔。因此,掌握最基本的動畫是完成酷炫開發之旅的根本。

作為動畫篇的第二篇文章,我在從UIView動畫說起簡單介紹了關於UIView的幾種基本動畫,這幾種動畫的搭配讓我們的登入介面富有靈性生動,但是這幾種動畫總是無法滿足我們對於動畫的需求。同樣的,本文將從一個小demo開始講解強大的transform動畫以及主要畫面格keyFrame動畫。

demo動效圖


可以看到兩個動畫:葉子被風吹落以及左邊的文字從summer變化到autumn,這兩個動畫都是基於強大的transform形變,其中葉子的飄落動畫通過主要畫面格動畫實現。demo連結

transform動畫

transform是一個非常重要的屬性,它在矩陣變換的層面上改變視圖的顯示效果,完成旋轉、形變、平移等等操作。在它被修改的同時,視圖的frame也會被真實改變。有兩個資料類型用來表示transform,分別是CGAffineTransformCATransform3D。前者作用於UIView,後者為layer層次的變換類型。基於後者可以實現更加強大的功能,但我們需要先掌握CGAffineTransform類型的使用。同時,本文講解也是這個變換類型。

talk is cheap show you the code

在開始使用transform實現你的動畫之前,我先介紹幾個常用的函數:

///用來串連兩個變換效果並返回。返回的t=t1*t2 CGAffineTransformConcat(CGAffineTransformt1,CGAffineTransformt2)   ///矩陣初始值。[100100] CGAffineTransformIdentity   ///自訂矩陣變換,需要掌握矩陣變換的知識才知道怎麼用。參照上面推薦的原理連結 CGAffineTransformMake(CGFloata,CGFloatb,CGFloatc,CGFloatd,CGFloattx,CGFloatty)   ///旋轉視圖。傳入參數為角度*(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左下角文字的變形過程記錄下來。這裡推薦mac上面的一款截取動圖的程式licecap,非常簡單好用。博主用它來分解動畫步驟,然後進行重現。

文字變形過程


不難看出在文字的動畫中做了兩個處理:y軸上的形變縮小、透明度的漸層過程。首先在項目中新增兩個UILabel,分別命名為label1、label2.然後在viewDidAppear中加入這麼一段代碼:

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

這裡解釋一下為什麼label2為什麼在動畫中y軸逐漸縮小為0.1而不是0。如果我們設為0的話,那麼在動畫提交之後,label2會直接保持動畫結束的狀態(這是出於效能最佳化自動完成的),因此在使用任何縮小的形變時,你可以將縮小值設定的很小,只要不是0。

運行你的代碼,文字的形變過程你已經做出來了,但是demo中的動畫不僅僅是形變,還包括位移的過程。很顯然,我們可以通過改變center的位置來實現這個效果,但這顯然不是我們今天想要的結果,實現新的動畫方式來實現更有意義。

動畫開始時形變出現的label高度為0,然後逐漸的的變高變為height,而label從頭到尾基於頂部的位置不發生改變。因此動畫開始前這個label在y軸上的位置是0,在完成顯示之後的y軸中心點為height / 2(基於label自身的座標系而言),那麼動畫的代碼就可以寫成這樣:

 -(void)viewDidAppear:(BOOL)animated{ ///初始化動畫開始前label的位置 CGFloatoffset=label1.frame.size.height*0.5;   label1.transform=CGAffineTransformConcat( CGAffineTransformMakeScale(0,0), CGAffineTransformTranslate(0,-offset) ); label1.alpha=0; [UIViewanimateWithDuration:3.animations:^{ ///還原label1的變換狀態並形變和位移label2 label1.transform=CGAffineTransformIdentifier; label1.transform=CGAffineTransformConcat( CGAffineTransformMakeScale(0,0), CGAffineTransformTranslate(0,offset) ); label1.alpha=1; label2.alpha=0; }]; }

調整兩個label的位置,並且設定其中一個透明顯示。然後運行這段代碼,你會發現文字轉變過程的動畫完成了。

keyframe動畫

將文章開頭的gif圖另存新檔到本地,然後使用預覽開啟看看,你會發現預覽中的gif圖變成了很多張的圖片。實際上,無論是動畫、電影、CG等動態效果,都可以看做是一張張圖片接連渲染實現的,而這些圖片切換的速度足夠快時我們就會當做是動畫。在此之前我們所講述的平移視圖在UIView動畫提交之後系統會根據動畫時間長度計算出視圖移動的所有幀介面,然後逐個渲染。

回到我們demo中的落葉動畫來,我總共對葉子的center進行過五次修改,我將落葉平移的線性路徑繪製出來並且標註關鍵的轉折點:

1.png


上面這個平移用UIView動畫代碼要如何?呢?毫無疑問,我們需要不斷的嵌套UIView動畫的使用來實現,具體代碼如下:

 [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:nilduration:0.6]; }duration:1.2]; }duration:1.2]; }duration:0.6]; }duration:0.4];   -(void)moveLeafWithOffset:(CGPoint)offsetcompletion:(void(^)(BOOLfinished))completionduration:(NSTimeInterval)duration { [UIViewanimateWithDuration:durationdelay:0options:UIViewAnimationOptionCurveLinearanimations:^{ CGPointcenter=_leaf.center; center.x+=offset.x; center.y+=offset.y; _leaf.center=center; }completion:completion]; }

看起來還蠻容易的,上面的代碼只是移動葉子,在gif圖中我們的葉子還有旋轉,因此我們還需要加上這麼一段代碼:

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

那麼ok,運行這段代碼看看,落葉的移動非常的生硬,我們可以明顯的看到拐角。其次,這段代碼中的duration傳入是沒有任何意義的(傳入一個固定的動畫時間長度無法體現出在落葉飄下這一過程中的層次步驟)

對於這兩個問題,UIView也提供了另一種動畫方式來協助我們解決這兩個問題 —— keyframe動畫:

 +(void)animateKeyframesWithDuration:(NSTimeInterval)durationdelay:(NSTimeInterval)delayoptions:(UIViewKeyframeAnimationOptions)optionsanimations:(void(^)(void))animationscompletion:(void(^__nullable)(BOOLfinished))completion +(void)addKeyframeWithRelativeStartTime:(double)frameStartTimerelativeDuration:(double)frameDurationanimations:(void(^)(void))animations

第一個方法是建立一個主要畫面格動畫,第二個方法用於在動畫的代碼塊中插入主要畫面格動畫資訊,兩個參數的意義表示如下:

frameStartTime 表示主要畫面格動畫開始的時刻在整個動畫中的百分比

frameDuration 表示這個主要畫面格動畫佔用整個動畫時間長度的百分比。

我做了一張圖片來表示參數含義:

  • 添加主要畫面格方法參數說明


    對比UIView動畫跟主要畫面格動畫,主要畫面格動畫引入了動畫佔比時間長度的概念,這讓我們能控制每個主要畫面格動畫的佔用比例而不是傳入一個無意義的動畫時間長度 —— 這讓我們的代碼更加難以理解。當然,除了動畫佔比之外,主要畫面格動畫的options參數也讓動畫變得更加平滑,下面是主要畫面格特有的配置參數:

     UIViewKeyframeAnimationOptionCalculationModeLinear//連續運算模式,線性 UIViewKeyframeAnimationOptionCalculationModeDiscrete//離散運算模式,只顯示主要畫面格 UIViewKeyframeAnimationOptionCalculationModePaced//均勻執行運算模式,線性 UIViewKeyframeAnimationOptionCalculationModeCubic//平滑運算模式 UIViewKeyframeAnimationOptionCalculationModeCubicPaced//平滑均勻運算模式

    在demo中我使用的是UIViewKeyframeAnimationOptionCalculationModeCubic,這個參數使用了貝茲路徑讓落葉的下落動畫變得更加平滑。效果可見最開始的gif動畫,你可以修改demo傳入的不同參數來查看效果。接下來我們就根據新的方法把上面的UIView動畫轉換成主要畫面格動畫代碼,具體代碼如下:

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

    可以看到相比UIView的動畫,主要畫面格動畫更加直觀的讓我們明白每一次平移動畫的時間佔比,代碼也相對的更加簡潔。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.