日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android uber启动动画,模仿Uber的启动画面(上)

發布時間:2025/3/8 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android uber启动动画,模仿Uber的启动画面(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

啟動畫面(Splash Screen)——不但給開發者們提供了一個盡情發揮、創建有趣動畫的機會,也填補了App啟動時從終端慢吞吞地下載數據的時間。啟動畫面(動態的)對于App至關重要:它可以讓用戶不失興趣地耐心等待應用完成加載。

盡管現在的啟動畫面多種多樣,但很少有像Uber這般精美的。2016年第一季度,Uber的CEO發表了關于重塑品牌的策略,其中之一就是現在這個超酷的啟動畫面。

這篇教程的目的是盡可能真實地再現Uber的動畫。我們會大量地使用到CALayers、CAAnimations,以及它們的子類。我不會從頭介紹這些類的基本概念,而是把重點放在如何應用這些類,創建高質量的動畫。如果你想要了解動畫背后的基本原理,可以參考Marin Todorove的iOS動畫中級教程。

入門

由于有非常多的動畫要實現,我們不妨在這個初始項目的基礎上進行修改。初始項目里已經為你創建好了所有需要的CALayer,我們給它們添加動畫即可。譯者注:為了保持教程簡潔,刪除了原文里一些與教學無關的文字。如有興趣可通過文章最后的鏈接閱讀原文相關內容。

先來看一眼最終效果:

打開初始項目看看里面的文件。

從控制器的角度分析,項目中的SplashViewController通過它的父視圖控制器RootContainerViewController生成。SplashViewController會不停循環播放啟動動畫,直到App完全加載完成,即與終端API握手成功并獲取了必要的數據。值得一提的是,在這個示例項目里,啟動動畫抽象成了一個單獨的模塊(譯者注:可以直接集成到其他項目里)。

RootContainerViewController里有兩個方法:showSplashViewController()和showSplashViewControllerNoPing()。我們主要使用第二個方法,它只會不停循環播放動畫(不會進入主界面),便于我們把精力集中在SplashViewController的子視圖上。當然,最后我們還是會切換回第一個方法,模擬API延遲并過渡到主界面。

啟動畫面的視圖和層

SplashViewController包含了兩個子視圖。其一是“波紋格子”背景,我們把它叫做TileGridView, ? ?由一系列TileView組成。另一個是帶有動畫效果的“U”字Logo,我們把它叫做AnimatedULogoView。

AnimatedULogoView里有4個CAShapeLayer:circleLayer:“U”型Logo的圓形白色背景

lineLayer:circleLayer中心到邊界的一條直線

squareLayer:circleLayer中心位置的正方形

maskLayer:遮罩層,當它的邊界隨著動畫改變的時會遮蓋其他層

這些CAShapeLayer組合在一起,構成了Fuber標志性的“U”。

既然已經知道這些層的組合方式,那我們可以開始創建動畫,讓AnimatedULogoView動起來了。

圓形的動畫

制作動畫的時候,最好過濾掉其他視覺“噪音”,只關注當前實現的動畫。打開AnimatedULogoView.swift,在init(frame:)方法里,把除了cricleLayer的其他層全都注釋掉,實現完動畫后我們會重新添加回來。注釋后的代碼應該像下面一樣:override?init(frame:?CGRect)?{??super.init(frame:?frame)

circleLayer?=?generateCircleLayer()

lineLayer?=?generateLineLayer()

squareLayer?=?generateSquareLayer()

maskLayer?=?generateMaskLayer()??//??layer.mask?=?maskLayer

layer.addSublayer(circleLayer)??//??layer.addSublayer(lineLayer)

//??layer.addSublayer(squareLayer)}

定位到generateCricleLayer()方法,試著理解一下這里的圓是怎么繪制的。其實它只不過是用UIBezierPath繪制的一個CAShapeLayer。注意這一行:layer.path?=?UIBezierPath(arcCenter:?CGPointZero,?radius:?radius/2,?startAngle:?-CGFloat(M_PI_2),?endAngle:?CGFloat(3*M_PI_2),?clockwise:?true).CGPath

默認情況下,如果你把startAngle設置為0,圓弧會從右側開始繪制(3點鐘方向)。如果設置為-M_PI_2,也就是-90°的話則會從上方開始繪制,endAngle最終是270°,或者說3*M_PI_2,同樣也是圓的正上方。另外要注意的是,在這里我們把弧線的寬度lineWidth設置為圓的半徑radius,因為我們想讓它動起來(畫圓的過程)。

circleLayer的動畫是由三個CAAnimation組成的:一個描繪筆端動畫的CAKeyframeAnimation,一個進行圖形變換的CABasicAnimation,以及一個CAAnimationGroup把它們合成在一起。

定位到animateCricleLayer(),添加下面代碼://?筆畫變化的動畫let?strokeEndAnimation?=?CAKeyframeAnimation(keyPath:?"strokeEnd")

strokeEndAnimation.timingFunction?=?strokeEndTimingFunction

strokeEndAnimation.duration?=?kAnimationDuration?-?kAnimationDurationDelay

strokeEndAnimation.values?=?[0.0,?1.0]

strokeEndAnimation.keyTimes?=?[0.0,?1.0]

通過把動畫的values設置為0.0和1.0,我們告訴Core Animation,從startAngle開始,到endAngle結束,創建像時鐘一樣的動畫。隨社storkeEnd的值變大,沿著周長的弧線長度也逐漸增加,整個圓逐漸被填滿。對于這個例子,如果你把values改為[0.0, 0.5],那么整個動畫只會填滿半個圓。

接著添加形變動畫:let?transformAnimation?=?CABasicAnimation(keyPath:?"transform")

transformAnimation.timingFunction?=?strokeEndTimingFunction

transformAnimation.duration?=?kAnimationDuration?-?kAnimationDurationDelay//?旋轉放大的動畫//?起始時:逆時針旋轉45°,x、y為正常大小的0.25倍var?startingTransform?=?CATransform3DMakeRotation(-CGFloat(M_PI_4),?0,?0,?1)

startingTransform?=?CATransform3DScale(startingTransform,?0.25,?0.25,?1)

transformAnimation.fromValue?=?NSValue(CATransform3D:?startingTransform)

transformAnimation.toValue?=?NSValue(CATransform3D:?CATransform3DIdentity)

這個動畫既包括了圖形的縮放也包括了沿z軸的旋轉。其結果是circleLayer在順時針旋轉45°的同時逐漸放大。這里旋轉的參數設置非常重要,因為和其它層的動畫組合的時候,它需要和lineLayer的位置及速度相匹配。

最后,在方法的末尾添加一個CAAnimationGroup,它負責把前面兩個動畫合成在一起,這樣你只需給cricleLayer添加一個動畫即可。//?把兩個動畫合成let?groupAnimation?=?CAAnimationGroup()

groupAnimation.animations?=?[strokeEndAnimation,?transformAnimation]

groupAnimation.repeatCount?=?Float.infinity?//?無限重復動畫groupAnimation.duration?=?kAnimationDuration

groupAnimation.beginTime?=?beginTime

groupAnimation.timeOffset?=?startTimeOffset

circleLayer.addAnimation(groupAnimation,?forKey:?"looping")

CAAnimationGroup設定了兩個重要的屬性:beginTime和timeOffset,如果你對它們不熟悉的話可以參考這篇文章,里面有這兩個屬性的描述以及用法。

這里的groupAnimation的beginTime屬性是根據父視圖的時間設定的。

timeOffset在這里也需要設定,因為這個動畫在第一次運行的時候,實際上是從中途開始的。當我們完成更多動畫時,你可以回到這里,嘗試改變startTimeOffset的值并觀察效果的差別。

把groupAnimation添加給circleLayer,編譯運行一下看看目前的效果:

提示:試著刪除strokeEndAnimation或者transformAnimation,看看單獨的每一個動畫是什么樣的。在這篇教程里,你可以嘗試不同動畫的效果。你可能會驚奇地發現,不同是動畫組合竟能創建出如此意想不到的獨特視覺效果。

直線的動畫

現在我們已經完成circleLayer的動畫了,該開始說說lineLayer了。還是在AnimatedULogoView.swift里,定位到startAnimating()把除了animateLineLayer()以外的方法全部注釋掉。注釋完的代碼應該如下面所示:public?func?startAnimating()?{

beginTime?=?CACurrentMediaTime()

layer.anchorPoint?=?CGPointZero

//??animateMaskLayer()

//??animateCircleLayer()

animateLineLayer()??//??animateSquareLayer()}

此外還需要調整一下init(frame:),只顯示circleLayer和lineLayer:override?init(frame:?CGRect)?{??super.init(frame:?frame)

circleLayer?=?generateCircleLayer()

lineLayer?=?generateLineLayer()

squareLayer?=?generateSquareLayer()

maskLayer?=?generateMaskLayer()??//??layer.mask?=?maskLayer

layer.addSublayer(circleLayer)

layer.addSublayer(lineLayer)??//??layer.addSublayer(squareLayer)}

注釋完畢,定位到animateLineLayer()方法,實現下一組動畫效果://?線段寬度動畫let?lineWidthAnimation?=?CAKeyframeAnimation(keyPath:?"lineWidth")

lineWidthAnimation.values?=?[0.0,?5.0,?0.0]

lineWidthAnimation.timingFunctions?=?[strokeEndTimingFunction,?circleLayerTimingFunction]

lineWidthAnimation.duration?=?kAnimationDuration

lineWidthAnimation.keyTimes?=?[0.0,?1.0?-?kAnimationDurationDelay/kAnimationDuration,?1.0]

這個動畫會先增加lineLayer的寬度,隨后變回來。

添加下面代碼實現下一個動畫://?變形let?transformAnimation?=?CAKeyframeAnimation(keyPath:?"transform")

transformAnimation.timingFunctions?=?[strokeEndTimingFunction,?circleLayerTimingFunction]

transformAnimation.duration?=?kAnimationDuration

transformAnimation.keyTimes?=?[0.0,?1.0?-?kAnimationDurationDelay/kAnimationDuration,?1.0]//?和之前一樣的旋轉放大動畫var?transform?=?CATransform3DMakeRotation(-CGFloat(M_PI_4),?0.0,?0.0,?1.0)

transform?=?CATransform3DScale(transform,?0.25,?0.25,?1.0)//?先放大再縮小transformAnimation.values?=?[NSValue(CATransform3D:?transform),?????????????????????????????NSValue(CATransform3D:?CATransform3DIdentity),?????????????????????????????NSValue(CATransform3D:?CATransform3DMakeScale(0.15,?0.15,?1.0))]

和circleLayer的動畫非常相似,我們在這里也定義了一個沿z軸順時針旋轉的動畫。在這里,我們同樣對線條定義了一個縮放動畫:從原始大小的25%開始,先變為原始大小,緊接著變為原始大小的15%。

用CAAnimationGroup把它們合成到一起,添加到lineLayer里://?合成動畫let?groupAnimation?=?CAAnimationGroup()

groupAnimation.repeatCount?=?Float.infinity

groupAnimation.removedOnCompletion?=?falsegroupAnimation.duration?=?kAnimationDuration

groupAnimation.beginTime?=?beginTime

groupAnimation.animations?=?[lineWidthAnimation,?transformAnimation]

groupAnimation.timeOffset?=?startTimeOffset

lineLayer.addAnimation(groupAnimation,?forKey:?"looping")

編譯運行,觀察一下效果。

注意,在這里我們把線條的初始位置設置為了-M_PI_4,同時把keyTimes設置為了[0.0, 1.0 - kAnimationDurationDelay/kAnimationDuration, 1.0]。數組的第一個和最后一個元素顯而易見:0.0代表起始,1.0代表終止。為了得到中間時間點,我們需要計算出圓形動畫完成、后半部分動畫開始(縮小的動畫)的時間。用kAnimationDurationDelay除以kAnimationDuration可以得到我們需要的結果。但因為它是個延遲動畫,所以我們應該用1.0減去它,我們需要從末尾往前倒,減去延遲時間。

現在我們已經完成了circleLayer和lineLayer的動畫了,接下來該處理中間的方形了。

方形的動畫

現在你應該已經輕車熟路了。定位到startAnimation()方法,注釋掉animateSquareLayer()以外的方法。并把init(frame:)方法修改成下面這樣:override?init(frame:?CGRect)?{??super.init(frame:?frame)

circleLayer?=?generateCircleLayer()

lineLayer?=?generateLineLayer()

squareLayer?=?generateSquareLayer()

maskLayer?=?generateMaskLayer()??//??layer.mask?=?maskLayer

layer.addSublayer(circleLayer)??//??layer.addSublayer(lineLayer)

layer.addSublayer(squareLayer)

}

修改完前往animateSquareLayer(),開始解決下一個動畫://?邊框let?b1?=?NSValue(CGRect:?CGRect(x:?0.0,?y:?0.0,?width:?2.0/3.0?*?squareLayerLength,?height:?2.0/3.0??*?squareLayerLength))let?b2?=?NSValue(CGRect:?CGRect(x:?0.0,?y:?0.0,?width:?squareLayerLength,?height:?squareLayerLength))let?b3?=?NSValue(CGRect:?CGRectZero)//?邊框從原始長度的2/3開始放大,到原始大小后再逐漸縮小到0let?boundsAnimation?=?CAKeyframeAnimation(keyPath:?"bounds")

boundsAnimation.values?=?[b1,?b2,?b3]

boundsAnimation.timingFunctions?=?[fadeInSquareTimingFunction,?squareLayerTimingFunction]

boundsAnimation.duration?=?kAnimationDuration

boundsAnimation.keyTimes?=?[0,?1.0-kAnimationDurationDelay/kAnimationDuration,?1.0]

上面的動畫改變了CALayer的邊框。我們創建了一個關鍵幀動畫,從邊長的2/3開始,放大到完整尺寸,再縮小到0。

接下來是背景顏色的動畫://?背景顏色的變化let?backgroundColorAnimation?=?CABasicAnimation(keyPath:?"backgroundColor")

backgroundColorAnimation.fromValue?=?UIColor.whiteColor().CGColorbackgroundColorAnimation.toValue?=?UIColor.fuberBlue().CGColorbackgroundColorAnimation.timingFunction?=?squareLayerTimingFunction

backgroundColorAnimation.fillMode?=?kCAFillModeBoth

backgroundColorAnimation.beginTime?=?kAnimationDurationDelay?*?2.0?/?kAnimationDuration

backgroundColorAnimation.duration?=?kAnimationDuration?/?(kAnimationDuration?-?kAnimationDurationDelay)

注意這里的fillMode屬性。由于beginTime不是零,動畫會把開始和結束時的CGColor包含進去。因此,當我們把動畫添加到父CAAnimationGroup里的時候不會閃現不同顏色。

說到CAAnimationGroup,該實現它了://?合成動畫let?groupAnimation?=?CAAnimationGroup()

groupAnimation.animations?=?[boundsAnimation,?backgroundColorAnimation]

groupAnimation.repeatCount?=?Float.infinity

groupAnimation.duration?=?kAnimationDuration

groupAnimation.removedOnCompletion?=?falsegroupAnimation.beginTime?=?beginTime

groupAnimation.timeOffset?=?startTimeOffset

squareLayer.addAnimation(groupAnimation,?forKey:?"looping")

編譯運行,檢查一下我們的進度,嗯看來方形動畫已經順利完成了。

是時候把之前的動畫合并到一起看看效果了!提示:在iOS模擬器里的動畫可能會有些卡頓,因為我們需要在Mac上模擬平時由iOS的GPU完成的工作。如果你的電腦不能流暢運行動畫,試著縮小模擬器的屏幕大小,或者在真機上測試。

遮罩層

首先取消init(frame:)和startAnimating()里所有注釋。

把所有動畫組合到一起,我們重新編譯運行一下Fuber。

看起來好像還是差點意思?cricleLayer的縮小消失太過突然。幸運的是,遮罩層動畫可以修正這個問題,讓它平滑地縮小。

定位到animateMaskLayer()添加下面的代碼://?邊框縮小let?boundsAnimation?=?CABasicAnimation(keyPath:?"bounds")

boundsAnimation.fromValue?=?NSValue(CGRect:?CGRect(x:?0.0,?y:?0.0,?width:?radius?*?2.0,?height:?radius?*?2))

boundsAnimation.toValue?=?NSValue(CGRect:?CGRect(x:?0.0,?y:?0.0,?width:?2.0/3.0?*?squareLayerLength,?height:?2.0/3.0?*?squareLayerLength))

boundsAnimation.duration?=?kAnimationDurationDelay

boundsAnimation.beginTime?=?kAnimationDuration?-?kAnimationDurationDelay

boundsAnimation.timingFunction?=?circleLayerTimingFunction

上面的代碼用于設定遮罩層邊界動畫。別忘了,當遮罩層的邊界改變時,整個AnimatedULogoView都會被遮擋,因為它作用于所有子層。

現在我們來實現圓角動畫,保持遮罩是圓形的://?邊角弧度let?cornerRadiusAnimation?=?CABasicAnimation(keyPath:?"cornerRadius")

cornerRadiusAnimation.beginTime?=?kAnimationDuration?-?kAnimationDurationDelay

cornerRadiusAnimation.duration?=?kAnimationDurationDelay

cornerRadiusAnimation.fromValue?=?radius

cornerRadiusAnimation.toValue?=?2cornerRadiusAnimation.timingFunction?=?circleLayerTimingFunction

把這兩個動畫合成為一個CAAnimationGroup,這個層就完成了://?合成動畫let?groupAnimation?=?CAAnimationGroup()

groupAnimation.removedOnCompletion?=?falsegroupAnimation.fillMode?=?kCAFillModeBoth

groupAnimation.beginTime?=?beginTime

groupAnimation.repeatCount?=?Float.infinity

groupAnimation.duration?=?kAnimationDuration

groupAnimation.animations?=?[boundsAnimation,?cornerRadiusAnimation]

groupAnimation.timeOffset?=?startTimeOffset

maskLayer.addAnimation(groupAnimation,?forKey:?"looping")

編譯運行。

看起來不錯!

教程的上半部分至此結束,關于背景網格的水波效果會在下一篇教程中介紹。

總結

以上是生活随笔為你收集整理的android uber启动动画,模仿Uber的启动画面(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。