杨锴
2024-11-06 63f7ed967433acee3ae8764c7a077e15c29c41f2
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//
//  CLRotateAnimationView.swift
//  CLPlayer
//
//  Created by Chen JmoVxia on 2021/10/27.
//
 
import UIKit
 
class CLRotateAnimationViewConfigure: NSObject {
    /// 开始起点
    var startAngle: CGFloat = -(.pi * 0.5)
    /// 开始结束点
    var endAngle: CGFloat = .pi * 1.5
    /// 动画总时间
    var duration: TimeInterval = 2
    /// 动画间隔时间
    var intervalDuration: TimeInterval = 0.12
    /// 小球个数
    var number: NSInteger = 5
    /// 小球直径
    var diameter: CGFloat = 8
    /// 小球背景颜色
    var backgroundColor: UIColor = .white
 
    fileprivate class func defaultConfigure() -> CLRotateAnimationViewConfigure {
        let configure = CLRotateAnimationViewConfigure()
        return configure
    }
}
 
class CLRotateAnimationView: UIView {
    /// 默认配置
    private let configure = CLRotateAnimationViewConfigure.defaultConfigure()
    /// layer数组
    private var layerArray: [CALayer] = Array()
    /// 是否开始动画
    var isStart: Bool = false
    /// 是否暂停
    private var isPause: Bool = false
 
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
 
    @available(*, unavailable)
    required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
 
    private func animation() {
        let origin_x: CGFloat = frame.size.width * 0.5
        let origin_y: CGFloat = frame.size.height * 0.5
        for item in 0 ..< configure.number {
            // 创建layer
            let scale = CGFloat(configure.number + 1 - item) / CGFloat(configure.number + 1)
            let layer = CALayer()
            layer.backgroundColor = configure.backgroundColor.cgColor
            layer.frame = CGRect(x: -5000, y: -5000, width: scale * configure.diameter, height: scale * configure.diameter)
            layer.cornerRadius = scale * configure.diameter * 0.5
            // 运动路径
            let pathAnimation = CAKeyframeAnimation(keyPath: "position")
            pathAnimation.calculationMode = .paced
            pathAnimation.fillMode = .forwards
            pathAnimation.isRemovedOnCompletion = false
            pathAnimation.duration = (configure.duration) - Double((configure.intervalDuration) * Double(configure.number))
            pathAnimation.beginTime = Double(item) * configure.intervalDuration
            pathAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
            pathAnimation.path = UIBezierPath(arcCenter: CGPoint(x: origin_x, y: origin_y), radius: (frame.size.width - configure.diameter) * 0.5, startAngle: configure.startAngle, endAngle: configure.endAngle, clockwise: true).cgPath
            // 动画组,动画组时间长于单个动画时间,会有停留效果
            let group = CAAnimationGroup()
            group.animations = [pathAnimation]
            group.duration = configure.duration
            group.isRemovedOnCompletion = false
            group.fillMode = .forwards
            group.repeatCount = MAXFLOAT
 
            layer.add(group, forKey: "RotateAnimation")
            layerArray.append(layer)
        }
    }
 
    /// 更新配置
    func updateWithConfigure(_ configureBlock: ((CLRotateAnimationViewConfigure) -> Void)?) {
        configureBlock?(configure)
        let intervalDuration = CGFloat(CGFloat(configure.duration) / 2.0 / CGFloat(configure.number))
        configure.intervalDuration = min(configure.intervalDuration, TimeInterval(intervalDuration))
        if isStart {
            stopAnimation()
            startAnimation()
        }
    }
}
 
extension CLRotateAnimationView {
    /// 开始动画
    func startAnimation() {
        if layerArray.isEmpty {
            animation()
            for item in layerArray {
                layer.addSublayer(item)
            }
            isStart = true
        }
    }
 
    /// 停止动画
    func stopAnimation() {
        for item in layerArray {
            item.removeFromSuperlayer()
        }
        layerArray.removeAll()
        isStart = false
    }
 
    /// 暂停动画
    func pauseAnimation() {
        if isPause {
            return
        }
        isPause = true
        // 取出当前时间,转成动画暂停的时间
        let pausedTime = layer.convertTime(CACurrentMediaTime(), from: nil)
        // 设置动画运行速度为0
        layer.speed = 0.0
        // 设置动画的时间偏移量,指定时间偏移量的目的是让动画定格在该时间点的位置
        layer.timeOffset = pausedTime
    }
 
    /// 恢复动画
    func resumeAnimation() {
        if !isPause {
            return
        }
        isPause = false
        // 获取暂停的时间差
        let pausedTime = layer.timeOffset
        layer.speed = 1.0
        layer.timeOffset = 0.0
        layer.beginTime = 0.0
        // 用现在的时间减去时间差,就是之前暂停的时间,从之前暂停的时间开始动画
        let timeSincePause = layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
        layer.beginTime = timeSincePause
    }
}