剛看到這個(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):
在寫這個(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
看到這個(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í)行完后就跟上圖一樣的效果了~~~
經(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í)行
這里我們使用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
這里我們還是使用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
這里我們也是使用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
關(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
動(dòng)畫看起來(lái)挺復(fù)雜肾砂,但是細(xì)細(xì)劃分出來(lái)也就那么回事,在寫動(dòng)畫之前要先想好動(dòng)畫的步驟床未,這個(gè)很關(guān)鍵裂允,希望大家通過(guò)這篇博客可以學(xué)到東西逗堵,有什么好的建議可以隨時(shí)提出來(lái)纸泡,謝謝大家閱讀~~demo地址