ios 动画
刚看到这个动画的时候,脑海里出现了两个方案,一种是通过drawRect画出来,然后配合CADisplayLink不停的绘制线的样式;第二种是通过CAShapeLayer配合CAAnimation来实现动画效果。再三考虑觉得使用后者,因为前者需要计算很多,比较复杂,而且经过测试前者相比于后者消耗更多的CPU,下面将我的思路写下来:
在写这个动画之前,我们把先需要的属性写好,比如线条的粗细,动画的时间等等,下面是相关的配置和初识化方法:
//线的宽度varlineWidth:CGFloat =0//线的长度varlineLength:CGFloat =0//边距varmargin:CGFloat =0//动画时间varduration:Double =2//动画的间隔时间varinterval:Double =1//四条线的颜色varcolors:[UIColor] = [UIColor.init(rgba:"#9DD4E9") , UIColor.init(rgba:"#F5BD58"), UIColor.init(rgba:"#FF317E") , UIColor.init(rgba:"#6FC9B5")]//动画的状态private(set)varstatus:AnimationStatus = .Normal//四条线privatevarlines:[CAShapeLayer] = []enumAnimationStatus {//普通状态caseNormal//动画中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
看到这个线条我就想到了用CAShapeLayer来处理,因为CAShapeLayer完全可以实现这种效果,而且它的strokeEnd的属性可以用来实现线条的长度变化的动画,下面上绘制四根线条的代码:
/**
线的第一步动画,线长从长变短
*/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基础动画,keyPath作用于线条的transform.translation.x和transform.translation.y属性,来实现向中间聚拢的效果,下面是效果图和代码:
/**
线的第二步动画,线向中间平移
*/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
这里我们还是使用CABasicAnimation基础动画,keyPath作用于线条的strokeEnd属性,让strokeEnd从0到1来实现线条长短的动画,下面是效果图和代码:
/**
线的第三步动画,线由短变长
*/privatefunclineAnimationThree() {//线移动的动画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
关于动画组合我没用到CAAnimationGroup,因为这些动画并不是加到同一个layer上,再加上动画类型有点多加起来也比较麻烦,我就通过动画的beginTime属性来控制动画的执行顺序,还加了动画暂停和继续的功能,效果和代码见下图:
//MARK: Public Methods/**
开始动画
*/func startAnimation() { angleAnimation() lineAnimationOne() lineAnimationTwo() lineAnimationThree() }/**
暂停动画
*/func pauseAnimation() { layer.pauseAnimation()forlineLayer in lines { lineLayer.pauseAnimation() } status = .pause }/**
继续动画
*/func resumeAnimation() { layer.resumeAnimation()forlineLayer in lines { lineLayer.resumeAnimation() } status = .Animating } extension CALayer {//暂停动画func pauseAnimation() {// 将当前时间CACurrentMediaTime转换为layer上的时间, 即将parent time转换为localtimelet pauseTime = convertTime(CACurrentMediaTime(), fromLayer: nil)// 设置layer的timeOffset, 在继续操作也会使用到timeOffset = pauseTime// localtime与parenttime的比例为0, 意味着localtime暂停了speed =0; }//继续动画func resumeAnimation() { let pausedTime = timeOffset speed =1timeOffset =0; beginTime =0// 计算暂停时间let sincePause = convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime// local time相对于parent time时间的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
动画看起来挺复杂,但是细细划分出来也就那么回事,在写动画之前要先想好动画的步骤,这个很关键,希望大家通过这篇博客可以学到东西,有什么好的建议可以随时提出来,谢谢大家阅读~~demo地址