【问题标题】:How to make circular loading animation color go from red to green as it loads如何使循环加载动画颜色在加载时从红色变为绿色
【发布时间】:2022-01-13 07:46:58
【问题描述】:

我已经在这个视频中实现了一个循环加载动画:

https://www.youtube.com/watch?v=O3ltwjDJaMk

这是我的代码:

class ViewController: UIViewController {

let shapeLayer = CAShapeLayer()

override func viewDidLoad() {
        super.viewDidLoad()
        
        let center = view.center
        let trackLayer = CAShapeLayer()
        
        let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
        trackLayer.path = circularPath.cgPath
        trackLayer.strokeColor = UIColor.lightGray.cgColor
        trackLayer.lineWidth = 10
        trackLayer.fillColor = UIColor.clear.cgColor
        trackLayer.lineCap = kCALineCapRound
        view.layer.addSublayer(trackLayer)
        
        shapeLayer.path = circularPath.cgPath
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineCap = kCALineCapRound
        shapeLayer.strokeEnd = 0
     
        view.layer.addSublayer(shapeLayer)
        
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
    }
    
    @objc private func handleTap() {
 
        let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
        
        basicAnimation.toValue = 1
        basicAnimation.duration = 2
        
        basicAnimation.fillMode = kCAFillModeForwards
        basicAnimation.isRemovedOnCompletion = false
        
        shapeLayer.add(basicAnimation, forKey: "urSoBasic")
    }
}

当动画发生时,我希望圆圈的颜色从红色变为绿色,如果圆圈完全完成,它应该是绿色,如果不是,则红绿之间的某种颜色(取决于用户 ID 中的变量)。 Any1知道如何做到这一点?感谢您的帮助:)

【问题讨论】:

    标签: ios swift xcode animation cashapelayer


    【解决方案1】:

    您也可以使用CAAnimationanimate color。所以你可以点击那个链接。

    您缺少的部分是计算需要根据当前值插值的fromto 颜色。

    假设您的进度视图将在某个时候更新界面以具有

    var minimumValue: CGFloat = 0.0
    var maximumValue: CGFloat = 100.0
    var currentValue: CGFloat = 30.0 // 30%
    

    然后使用这些值,您可以计算当前进度,01 之间的值应该定义颜色插值比例。计算它的基本数学应该是:

    let progress = (currentValue-maximumValue)/(minimumValue-maximumValue) // TODO: handle division by zero. Handle current value out of bounds.
    

    随着进度,您现在可以使用插入任何数值

    func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
        return values.from + (values.to - values.from)*scale
    }
    

    但颜色不是数值。在CAAnimation 的情况下,您应该将其分解为 4 个数值作为 RGBA 以保持一致性(至少我相信 CAAnimation 在 RGBA 空间中使用颜色插值)。就像一个注释;在许多情况下,当您在 HSV 空间而不是 RGB 中进行插值时,它看起来会更好。

    所以插值颜色应该是这样的:

    func rgba(_ color: UIColor) -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
        var r: CGFloat = 0.0
        var g: CGFloat = 0.0
        var b: CGFloat = 0.0
        var a: CGFloat = 0.0
        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        return (r, g, b, a)
    }
    func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
        return values.from + (values.to - values.from)*scale
    }
    func interpolate(_ values: (from: UIColor, to: UIColor), scale: CGFloat) -> UIColor {
        let startColorComponents = rgba(values.from)
        let endColorComponents = rgba(values.to)
        return .init(red: interpolate((startColorComponents.r, endColorComponents.r), scale: scale),
                     green: interpolate((startColorComponents.g, endColorComponents.g), scale: scale),
                     blue: interpolate((startColorComponents.b, endColorComponents.b), scale: scale),
                     alpha: interpolate((startColorComponents.a, endColorComponents.a), scale: scale))
    }
    

    这些应该是您制作动画所需的所有组件,我希望它们足以让您走上正轨。我个人喜欢有更多的控制权,而且我喜欢避免使用CAAnimation 甚至形状图层等工具。所以这就是我将如何完成你的任务:

    class ViewController: UIViewController {
    
        @IBOutlet private var progressView: ProgressView?
        
        override func viewDidLoad() {
            super.viewDidLoad()
         
            view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
        }
        
        @objc private func onTap() {
            guard let progressView = progressView else { return }
            progressView.animateValue(to: .random(in: progressView.minimumValue...progressView.maximumValue), duration: 0.3)
        }
    
    
    }
    
    @IBDesignable class ProgressView: UIView {
        
        @IBInspectable var minimumValue: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
        @IBInspectable var maximumValue: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
        @IBInspectable var value: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
        @IBInspectable var colorAtZeroProgress: UIColor = .black { didSet { setNeedsDisplay() } }
        @IBInspectable var colorAtFullProgress: UIColor = .black { didSet { setNeedsDisplay() } }
        @IBInspectable var lineWidth: CGFloat = 10.0 { didSet { setNeedsDisplay() } }
        
        private var currentTimer: Timer?
        func animateValue(to: CGFloat, duration: TimeInterval) {
            currentTimer?.invalidate()
            let startTime = Date()
            let startValue = self.value
            currentTimer = Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true, block: { timer in
                let progress = CGFloat(max(0.0, min(Date().timeIntervalSince(startTime)/duration, 1.0)))
                if progress >= 1.0 {
                    // End of animation
                    timer.invalidate()
                }
                self.value = startValue + (to - startValue)*progress
            })
        }
        
        override func draw(_ rect: CGRect) {
            super.draw(rect)
             
            let progress = max(0.0, min((value-minimumValue)/(maximumValue-minimumValue), 1.0))
            
            let center = CGPoint(x: bounds.midX, y: bounds.midY)
            let radius = min(bounds.width, bounds.height)*0.5 - lineWidth*0.5
            let startAngle: CGFloat = -.pi*2.0
            let endAngle: CGFloat = startAngle + .pi*2.0*progress
            let circularPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
            
            
            
            let interpolatedColor: UIColor = {
                func rgba(_ color: UIColor) -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
                    var r: CGFloat = 0.0
                    var g: CGFloat = 0.0
                    var b: CGFloat = 0.0
                    var a: CGFloat = 0.0
                    color.getRed(&r, green: &g, blue: &b, alpha: &a)
                    return (r, g, b, a)
                }
                func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
                    return values.from + (values.to - values.from)*scale
                }
                func interpolate(_ values: (from: UIColor, to: UIColor), scale: CGFloat) -> UIColor {
                    let startColorComponents = rgba(values.from)
                    let endColorComponents = rgba(values.to)
                    return .init(red: interpolate((startColorComponents.r, endColorComponents.r), scale: scale),
                                 green: interpolate((startColorComponents.g, endColorComponents.g), scale: scale),
                                 blue: interpolate((startColorComponents.b, endColorComponents.b), scale: scale),
                                 alpha: interpolate((startColorComponents.a, endColorComponents.a), scale: scale))
                }
                
                return interpolate((self.colorAtZeroProgress, self.colorAtFullProgress), scale: progress)
            }()
            
            interpolatedColor.setStroke()
            circularPath.lineCapStyle = .round
            circularPath.lineWidth = lineWidth
            circularPath.stroke()
        }
        
        
    }
    

    随意使用它并随意修改它。

    【讨论】:

    • 哇,谢谢兄弟!!
    猜你喜欢
    • 1970-01-01
    • 2021-07-07
    • 2017-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-10
    相关资源
    最近更新 更多