簡體   English   中英

CABasicAnimation CALayer Path Animation 方向

[英]CABasicAnimation CALayer Path Animation Direction

我正在嘗試使用CABasicAnimation鍵路徑設置圖層路徑屬性但是。 沒有得到准確的結果

我正在尋找的路徑 animation 的變化如下

預期 Animation

在此處輸入圖像描述

注意:-請忽略開關.. 我的重點只是從circularmoon animation 的路徑變化

我試過的

這是我試圖獲得此路徑轉換的代碼 animation

@IBDesignable class CustomView: UIView {

 private lazy var shapeLayer = CAShapeLayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
        
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        shapeLayer.frame = bounds
        shapeLayer.fillColor = UIColor.black.cgColor
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 3
        layer.addSublayer(shapeLayer)
        
    }
    
    override func layoutSubviews() {
        
        shapeLayer.path = drawSunShape(bounds)
    }
    private func drawSunShape(_ group:CGRect) -> CGPath {
        let bezierPath = UIBezierPath()
        let radius = group.maxX/2
        let center =  CGPoint(x: group.midX, y: group.midY)
        bezierPath.addArc(withCenter:center, radius: radius, startAngle: -.pi/2, endAngle: 3 * .pi * 0.5 , clockwise: true)
        return bezierPath.cgPath
    }
    
    private func drawMoonShape(_ group:CGRect) -> CGPath {
  
        let rad  : CGFloat = group.maxX/2
        let center =  CGPoint(x: group.midX, y: group.midY)
       
        
        let big = UIBezierPath()
       
        big.addArc(withCenter: center, radius: rad, startAngle:-.pi/2.0, endAngle: .pi/2.0, clockwise: true)
        big.addArc(withCenter: CGPoint(x: rad * cos(.pi/2.0), y: center.y), radius: rad * sin(.pi/2.0), startAngle: .pi/2.0, endAngle: -.pi/2.0, clockwise: false)
        big.close()
 
        
        return big.cgPath
    }
    private func animateToOffShape()  {
      
            let anim = CABasicAnimation(keyPath: "path")
            anim.toValue = drawMoonShape(bounds)
            anim.duration = 5.0
            shapeLayer.add(anim, forKey: nil)
        } 
    }

我從這段代碼中得到了什么

更新

我能夠在@sweeper 和@max 的幫助下實現這一目標

在此處輸入圖像描述

但我想要它從中間右邊

任何指針我如何能達到預期的結果。 謝謝

正如 Max 所說,當兩條路徑具有相同數量的點時,貝塞爾路徑插值效果最佳。

這可能會讓你接近你的目標......

這個想法是將圓分成兩條弧 - 保持一條弧不變,並修改另一條弧:

為了計算弧線,我們從一個完整的圓和我們想要的新月開始:

在此處輸入圖像描述

在此處輸入圖像描述

要獲得新月點,我們可以取原始的完整圓並將其中心向上和向左移動(半徑的 1/3 給出了不錯的結果):

在此處輸入圖像描述

所以,我們想把圓分成兩條弧......

在此處輸入圖像描述

我們的第一個弧將從 301° 和 go 順時針到 149° 開始:

在此處輸入圖像描述

這意味着我們的第二個弧將從 149° 開始,go 順時針到 301°:

在此處輸入圖像描述

下一步是獲得“修改后的”第二個弧:

在此處輸入圖像描述

這里我們的第二個圓弧將從 121° 開始,go逆時針到 329°:

在此處輸入圖像描述

結果:

在此處輸入圖像描述

在此處輸入圖像描述

這是一些示例代碼,帶有硬編碼的角度。 我將由您自己進行數學運算,以獲得不同新月形狀的精確角度。 每次點擊都會在“太陽”和“月亮”形狀之間來回動畫:

// little degrees-to-radians helper
extension BinaryInteger {
    var degreesToRadians: CGFloat { CGFloat(self) * .pi / 180 }
}

class MoonView: UIView {
    
    private lazy var shapeLayer = CAShapeLayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        shapeLayer.fillColor = UIColor.cyan.cgColor

        // to see outline only
        //shapeLayer.fillColor = UIColor.clear.cgColor
        
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 3
        shapeLayer.lineJoin = .round
        
        layer.addSublayer(shapeLayer)
    }
    
    private var isOn: Bool = true
    
    override func layoutSubviews() {
        super.layoutSubviews()
        shapeLayer.frame = bounds
        if isOn {
            shapeLayer.path = drawSunShape(bounds)
        } else {
            shapeLayer.path = drawMoonShape(bounds)
        }
    }
    
    private func drawSunShape(_ group:CGRect) -> CGPath {
        let radius = group.maxX/2
        let center =  CGPoint(x: group.midX, y: group.midY)
        
        let bezierPath = UIBezierPath()
        bezierPath.addArc(withCenter: center, radius: radius, startAngle: 301.degreesToRadians, endAngle: 149.degreesToRadians, clockwise: true)
        bezierPath.addArc(withCenter: center, radius: radius, startAngle: 149.degreesToRadians, endAngle: 301.degreesToRadians, clockwise: true)
        bezierPath.close()
        
        return bezierPath.cgPath
    }
    
    private func drawMoonShape(_ group:CGRect) -> CGPath {
        let radius: CGFloat = group.maxX/2
        
        let centerA =  CGPoint(x: group.midX, y: group.midY)
        let offset = radius * 1.0 / 3.0
        let centerB =  CGPoint(x: centerA.x - offset, y: centerA.y - offset)
        
        let bezierPath = UIBezierPath()
        bezierPath.addArc(withCenter: centerA, radius: radius, startAngle: 301.degreesToRadians, endAngle: 149.degreesToRadians, clockwise: true)
        bezierPath.addArc(withCenter: centerB, radius: radius, startAngle: 121.degreesToRadians, endAngle: 329.degreesToRadians, clockwise: false)
        bezierPath.close()
        
        return bezierPath.cgPath
    }
    func animateShape()  {
        let anim = CABasicAnimation(keyPath: "path")
        if isOn {
            anim.toValue = drawMoonShape(bounds)
        } else {
            anim.toValue = drawSunShape(bounds)
        }
        anim.duration = 0.5
        anim.fillMode = .forwards
        anim.isRemovedOnCompletion = false
        shapeLayer.add(anim, forKey: nil)
        isOn.toggle()
    }
}

class MoonViewController: UIViewController {
    
    let mView = MoonView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .yellow
        
        view.addSubview(mView)
        mView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(mView)
        NSLayoutConstraint.activate([
            mView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            mView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            mView.widthAnchor.constraint(equalToConstant: 200.0),
            mView.heightAnchor.constraint(equalTo: mView.widthAnchor)
        ])
        
        mView.backgroundColor = .white
        
        let t = UITapGestureRecognizer(target: self, action: #selector(self.didTap(_:)))
        view.addGestureRecognizer(t)
    }
    
    @objc func didTap(_ g: UITapGestureRecognizer) -> Void {
        mView.animateShape()
    }

}

如何定義這兩條路徑很重要。 iOS 不夠智能,無法在兩條任意路徑之間自動創建美觀的過渡。 兩者在建設中越接近,過渡就越好。

例如,如果你像這樣構建你的月亮

月亮路徑

你可以像這樣構建你的圈子

圓形路徑

請注意,兩個形狀都有四個圓弧,形成大部分圓圈的兩個大圓弧在兩個形狀之間是相同的(只是旋轉了一點)。 特別是如果你保持兩個形狀之間的弧順序一致,iOS 在創建 animation 方面會做得更好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM