ios 動(dòng)畫

剛看到這個(gè)動(dòng)畫的時(shí)候,腦海里出現(xiàn)了兩個(gè)方案旺垒,一種是通過(guò)drawRect畫出來(lái)眯搭,然后配合CADisplayLink不停的繪制線的樣式仗岸;第二種是通過(guò)CAShapeLayer配合CAAnimation來(lái)實(shí)現(xiàn)動(dòng)畫效果盗痒。再三考慮覺(jué)得使用后者君编,因?yàn)榍罢咝枰?jì)算很多亮瓷,比較復(fù)雜,而且經(jīng)過(guò)測(cè)試前者相比于后者消耗更多的CPU,下面將我的思路寫下來(lái):

相關(guān)配置和初始化方法

在寫這個(gè)動(dòng)畫之前,我們把先需要的屬性寫好,比如線條的粗細(xì)柱查,動(dòng)畫的時(shí)間等等雹熬,下面是相關(guān)的配置和初識(shí)化方法:

//線的寬度varlineWidth:CGFloat =0//線的長(zhǎng)度varlineLength:CGFloat =0//邊距varmargin:CGFloat =0//動(dòng)畫時(shí)間varduration:Double =2//動(dòng)畫的間隔時(shí)間varinterval:Double =1//四條線的顏色varcolors:[UIColor] = [UIColor.init(rgba:"#9DD4E9") , UIColor.init(rgba:"#F5BD58"),? UIColor.init(rgba:"#FF317E") , UIColor.init(rgba:"#6FC9B5")]//動(dòng)畫的狀態(tài)private(set)varstatus:AnimationStatus = .Normal//四條線privatevarlines:[CAShapeLayer] = []enumAnimationStatus {//普通狀態(tài)caseNormal//動(dòng)畫中caseAnimating//暫停casepause? ? }//MARK: Initial Methodsconvenience init(fram: CGRect , colors: [UIColor]) {? ? ? ? self.init()? ? ? ? self.frame = frame? ? ? ? self.colors = colors? ? ? ? config()? ? }overrideinit(frame: CGRect) {? ? ? ? super.init(frame: frame)? ? ? ? config()? ? }? ? required init?(coder aDecoder: NSCoder) {? ? ? ? super.init(coder: aDecoder)? ? ? ? config()? ? }privatefuncconfig() {? ? ? ? lineLength = max(frame.width, frame.height)? ? ? ? lineWidth? = lineLength/6.0margin? ? = lineLength/4.5+ lineWidth/2drawLineShapeLayer()? ? ? ? transform = CGAffineTransformRotate(CGAffineTransformIdentity, angle(-30))? ? }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

通過(guò)CAShapeLayer繪制線條

看到這個(gè)線條我就想到了用CAShapeLayer來(lái)處理诡壁,因?yàn)镃AShapeLayer完全可以實(shí)現(xiàn)這種效果宠漩,而且它的strokeEnd的屬性可以用來(lái)實(shí)現(xiàn)線條的長(zhǎng)度變化的動(dòng)畫,下面上繪制四根線條的代碼:

//MARK: 繪制線/**

繪制四條線

*/privatefuncdrawLineShapeLayer() {//開始點(diǎn)let startPoint = [point(lineWidth/2, y: margin),? ? ? ? ? ? ? ? ? ? ? ? ? point(lineLength - margin, y: lineWidth/2),? ? ? ? ? ? ? ? ? ? ? ? ? point(lineLength - lineWidth/2, y: lineLength - margin),? ? ? ? ? ? ? ? ? ? ? ? ? point(margin, y: lineLength - lineWidth/2)]//結(jié)束點(diǎn)let endPoint? = [point(lineLength - lineWidth/2, y: margin) ,? ? ? ? ? ? ? ? ? ? ? ? point(lineLength - margin, y: lineLength - lineWidth/2) ,? ? ? ? ? ? ? ? ? ? ? ? point(lineWidth/2, y: lineLength - margin) ,? ? ? ? ? ? ? ? ? ? ? ? point(margin, y: lineWidth/2)]fori in0...3{? ? ? ? ? ? let line:CAShapeLayer = CAShapeLayer()? ? ? ? ? ? line.lineWidth? = lineWidth? ? ? ? ? ? line.lineCap? ? = kCALineCapRound? ? ? ? ? ? line.opacity? ? =0.8line.strokeColor = colors[i].CGColor? ? ? ? ? ? line.path? ? ? ? = getLinePath(startPoint[i], endPoint: endPoint[i]).CGPath? ? ? ? ? ? layer.addSublayer(line)? ? ? ? ? ? lines.append(line)? ? ? ? }? ? }/**

獲取線的路徑

- parameter startPoint: 開始點(diǎn)

- parameter endPoint:? 結(jié)束點(diǎn)

- returns: 線的路徑

*/privatefuncgetLinePath(startPoint: CGPoint, endPoint: CGPoint) -> UIBezierPath {? ? ? ? let path = UIBezierPath()? ? ? ? path.moveToPoint(startPoint)? ? ? ? path.addLineToPoint(endPoint)returnpath? ? }privatefuncpoint(x:CGFloat , y:CGFloat) -> CGPoint {returnCGPointMake(x, y)? ? }privatefuncangle(angle: Double) -> CGFloat {returnCGFloat(angle *? (M_PI/180))? ? }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

執(zhí)行完后就跟上圖一樣的效果了~~~

動(dòng)畫分解

經(jīng)過(guò)分析,可以將動(dòng)畫分為四個(gè)步驟:

畫布的旋轉(zhuǎn)動(dòng)畫义桂,旋轉(zhuǎn)兩圈

線條由長(zhǎng)變短的動(dòng)畫陆赋,更畫布選擇的動(dòng)畫一起執(zhí)行嗅榕,旋轉(zhuǎn)一圈的時(shí)候結(jié)束

線條的位移動(dòng)畫,線條逐漸向中間靠攏佃乘,再畫筆旋轉(zhuǎn)完一圈的時(shí)候執(zhí)行,兩圈的時(shí)候結(jié)束

線條由短變長(zhǎng)的動(dòng)畫髓绽,畫布旋轉(zhuǎn)完兩圈的時(shí)候執(zhí)行

第一步畫布旋轉(zhuǎn)動(dòng)畫

這里我們使用CABasicAnimation基礎(chǔ)動(dòng)畫,keyPath作用于畫布的transform.rotation.z僵闯,以z軸為目標(biāo)進(jìn)行旋轉(zhuǎn)泳秀,下面是效果圖和代碼:

//MARK: 動(dòng)畫步驟/**

旋轉(zhuǎn)的動(dòng)畫,旋轉(zhuǎn)兩圈

*/privatefuncangleAnimation() {? ? ? ? let angleAnimation? ? ? ? ? ? ? ? = CABasicAnimation.init(keyPath:"transform.rotation.z")? ? ? ? angleAnimation.fromValue? ? ? ? ? = angle(-30)? ? ? ? angleAnimation.toValue? ? ? ? ? ? = angle(690)? ? ? ? angleAnimation.fillMode? ? ? ? ? ? = kCAFillModeForwards? ? ? ? angleAnimation.removedOnCompletion =falseangleAnimation.duration? ? ? ? ? ? = duration? ? ? ? angleAnimation.delegate? ? ? ? ? ? = self? ? ? ? layer.addAnimation(angleAnimation, forKey:"angleAnimation")? ? }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

第二步線條由長(zhǎng)變短的動(dòng)畫

這里我們還是使用CABasicAnimation基礎(chǔ)動(dòng)畫,keyPath作用于線條的strokeEnd屬性庆杜,讓strokeEnd從1到0來(lái)實(shí)現(xiàn)線條長(zhǎng)短的動(dòng)畫罗洗,下面是效果圖和代碼:

/**

線的第一步動(dòng)畫,線長(zhǎng)從長(zhǎng)變短

*/privatefunclineAnimationOne() {? ? ? ? let lineAnimationOne? ? ? ? ? ? ? ? = CABasicAnimation.init(keyPath:"strokeEnd")? ? ? ? lineAnimationOne.duration? ? ? ? ? ? = duration/2lineAnimationOne.fillMode? ? ? ? ? ? = kCAFillModeForwards? ? ? ? lineAnimationOne.removedOnCompletion =falselineAnimationOne.fromValue? ? ? ? ? =1lineAnimationOne.toValue? ? ? ? ? ? =0fori in0...3{? ? ? ? ? ? let lineLayer = lines[i]? ? ? ? ? ? lineLayer.addAnimation(lineAnimationOne, forKey:"lineAnimationOne")? ? ? ? }? ? }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

第三步線條的位移動(dòng)畫

這里我們也是使用CABasicAnimation基礎(chǔ)動(dòng)畫脱茉,keyPath作用于線條的transform.translation.x和transform.translation.y屬性锻梳,來(lái)實(shí)現(xiàn)向中間聚攏的效果国章,下面是效果圖和代碼:

/**

線的第二步動(dòng)畫撬统,線向中間平移

*/privatefunclineAnimationTwo() {foriin0...3{varkeypath ="transform.translation.x"ifi%2==1{? ? ? ? ? ? ? ? keypath ="transform.translation.y"}letlineAnimationTwo = CABasicAnimation.init(keyPath: keypath)? ? ? ? ? ? lineAnimationTwo.beginTime = CACurrentMediaTime() + duration/2lineAnimationTwo.duration = duration/4lineAnimationTwo.fillMode = kCAFillModeForwards? ? ? ? ? ? lineAnimationTwo.removedOnCompletion =falselineAnimationTwo.autoreverses =truelineAnimationTwo.fromValue =0ifi <2{? ? ? ? ? ? ? ? lineAnimationTwo.toValue = lineLength/4}else{? ? ? ? ? ? ? ? lineAnimationTwo.toValue = -lineLength/4}letlineLayer = lines[i]? ? ? ? ? ? lineLayer.addAnimation(lineAnimationTwo, forKey:"lineAnimationTwo")? ? ? ? }//三角形兩邊的比例letscale = (lineLength -2*margin)/(lineLength - lineWidth)foriin0...3{varkeypath ="transform.translation.y"ifi%2==1{? ? ? ? ? ? ? ? keypath ="transform.translation.x"}letlineAnimationTwo = CABasicAnimation.init(keyPath: keypath)? ? ? ? ? ? lineAnimationTwo.beginTime = CACurrentMediaTime() + duration/2lineAnimationTwo.duration = duration/4lineAnimationTwo.fillMode = kCAFillModeForwards? ? ? ? ? ? lineAnimationTwo.removedOnCompletion =falselineAnimationTwo.autoreverses =truelineAnimationTwo.fromValue =0ifi ==0|| i ==3{? ? ? ? ? ? ? ? lineAnimationTwo.toValue = lineLength/4* scale? ? ? ? ? ? }else{? ? ? ? ? ? ? ? lineAnimationTwo.toValue = -lineLength/4* scale? ? ? ? ? ? }letlineLayer = lines[i]? ? ? ? ? ? lineLayer.addAnimation(lineAnimationTwo, forKey:"lineAnimationThree")? ? ? ? }? ? }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

第四步線條恢復(fù)的原來(lái)長(zhǎng)度的動(dòng)畫

這里我們還是使用CABasicAnimation基礎(chǔ)動(dòng)畫愉昆,keyPath作用于線條的strokeEnd屬性刹勃,讓strokeEnd從0到1來(lái)實(shí)現(xiàn)線條長(zhǎng)短的動(dòng)畫,下面是效果圖和代碼:

/**

線的第三步動(dòng)畫,線由短變長(zhǎng)

*/privatefunclineAnimationThree() {//線移動(dòng)的動(dòng)畫let lineAnimationFour? ? ? ? ? ? ? ? = CABasicAnimation.init(keyPath:"strokeEnd")? ? ? ? lineAnimationFour.beginTime? ? ? ? ? ? = CACurrentMediaTime() + duration? ? ? ? lineAnimationFour.duration? ? ? ? ? ? = duration/4lineAnimationFour.fillMode? ? ? ? ? ? = kCAFillModeForwards? ? ? ? lineAnimationFour.removedOnCompletion =falselineAnimationFour.fromValue? ? ? ? ? =0lineAnimationFour.toValue? ? ? ? ? ? =1fori in0...3{ifi ==3{? ? ? ? ? ? ? ? lineAnimationFour.delegate = self? ? ? ? ? ? }? ? ? ? ? ? let lineLayer = lines[i]? ? ? ? ? ? lineLayer.addAnimation(lineAnimationFour, forKey:"lineAnimationFour")? ? ? ? }? ? }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

最后一步需要將動(dòng)畫組合起來(lái)

關(guān)于動(dòng)畫組合我沒(méi)用到CAAnimationGroup隶糕,因?yàn)檫@些動(dòng)畫并不是加到同一個(gè)layer上晾剖,再加上動(dòng)畫類型有點(diǎn)多加起來(lái)也比較麻煩,我就通過(guò)動(dòng)畫的beginTime屬性來(lái)控制動(dòng)畫的執(zhí)行順序躏救,還加了動(dòng)畫暫停和繼續(xù)的功能英妓,效果和代碼見(jiàn)下圖:

//MARK: Public Methods/**

開始動(dòng)畫

*/func startAnimation() {? ? ? ? angleAnimation()? ? ? ? lineAnimationOne()? ? ? ? lineAnimationTwo()? ? ? ? lineAnimationThree()? ? }/**

暫停動(dòng)畫

*/func pauseAnimation() {? ? ? ? layer.pauseAnimation()forlineLayer in lines {? ? ? ? ? ? lineLayer.pauseAnimation()? ? ? ? }? ? ? ? status = .pause? ? }/**

繼續(xù)動(dòng)畫

*/func resumeAnimation() {? ? ? ? layer.resumeAnimation()forlineLayer in lines {? ? ? ? ? ? lineLayer.resumeAnimation()? ? ? ? }? ? ? ? status = .Animating? ? }? ? extension CALayer {//暫停動(dòng)畫func pauseAnimation() {// 將當(dāng)前時(shí)間CACurrentMediaTime轉(zhuǎn)換為layer上的時(shí)間, 即將parent time轉(zhuǎn)換為localtimelet pauseTime = convertTime(CACurrentMediaTime(), fromLayer: nil)// 設(shè)置layer的timeOffset, 在繼續(xù)操作也會(huì)使用到timeOffset? ? = pauseTime// localtime與parenttime的比例為0, 意味著localtime暫停了speed? ? ? ? =0;? ? }//繼續(xù)動(dòng)畫func resumeAnimation() {? ? ? ? let pausedTime = timeOffset? ? ? ? speed? ? ? ? ? =1timeOffset? ? =0;? ? ? ? beginTime? ? ? =0// 計(jì)算暫停時(shí)間let sincePause = convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime// local time相對(duì)于parent time時(shí)間的beginTimebeginTime? ? ? = sincePause? ? }}//MARK: Animation Delegateoverridefunc animationDidStart(anim: CAAnimation) {iflet animation = anim as? CABasicAnimation {ifanimation.keyPath =="transform.rotation.z"{? ? ? ? ? ? ? ? status = .Animating? ? ? ? ? ? }? ? ? ? }? ? }overridefunc animationDidStop(anim: CAAnimation, finished flag: Bool) {iflet animation = anim as? CABasicAnimation {ifanimation.keyPath =="strokeEnd"{ifflag {? ? ? ? ? ? ? ? ? ? status = .Normal? ? ? ? ? ? ? ? ? ? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(interval) * Int64(NSEC_PER_SEC)), dispatch_get_main_queue(), {ifself.status != .Animating {? ? ? ? ? ? ? ? ? ? ? ? ? ? self.startAnimation()? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? })? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }? ? }//MARK: Overrideoverridefunc touchesEnded(touches: Set, withEvent event: UIEvent?) {? ? ? ? switch status {case.Animating:? ? ? ? ? ? pauseAnimation()case.pause:? ? ? ? ? ? resumeAnimation()case.Normal:? ? ? ? ? ? startAnimation()? ? ? ? }? ? }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

總結(jié)

動(dòng)畫看起來(lái)挺復(fù)雜肾砂,但是細(xì)細(xì)劃分出來(lái)也就那么回事,在寫動(dòng)畫之前要先想好動(dòng)畫的步驟床未,這個(gè)很關(guān)鍵裂允,希望大家通過(guò)這篇博客可以學(xué)到東西逗堵,有什么好的建議可以隨時(shí)提出來(lái)纸泡,謝謝大家閱讀~~demo地址

最后編輯于
?著作權(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)容