沿更大的圆弧路径移动圆弧段路径。 SWIFT

Moving arc segment path along bigger arc path. SWIFT

我正在尝试制作沿“母”弧路径(红色部分)移动弧段路径(绿色部分)的动画。

mainView

这里是完整的存储库:https://github.com/gmb55/movingArcs

完整代码:


import UIKit

class MovingArcs: UIView {
    var path: UIBezierPath!
    var shapeLayer: CAShapeLayer!
    var movement: CAKeyframeAnimation!

    // set path for moving and draw it on screen
    func pathShapeLayer() -> UIBezierPath {
        let startDegree = 180.0
        let endDegree = 360.0
        let start = CGFloat(startDegree).toRadians()
        let end = CGFloat(endDegree).toRadians()

        path = UIBezierPath(arcCenter: CGPoint(x: 100, y: 100),
            radius: 100.0,
            startAngle: start,
            endAngle: end,
            clockwise: true)

        let pathShapeLayer = CAShapeLayer()
        pathShapeLayer.path = path.cgPath
        pathShapeLayer.fillColor = UIColor.clear.cgColor
        pathShapeLayer.lineWidth = CGFloat(4)
        pathShapeLayer.strokeColor = UIColor.red.cgColor
        pathShapeLayer.position = .init(x: 0, y: 0)

        self.layer.addSublayer(pathShapeLayer)

        return path
    }

    // set movemnet along path with rotation
    func addAnimation() {
        let movement = CAKeyframeAnimation(keyPath: "position")
        movement.path = pathShapeLayer().cgPath
        movement.duration = 3
        movement.repeatCount = 1
        movement.rotationMode = CAAnimationRotationMode.rotateAuto
        movement.calculationMode = CAAnimationCalculationMode.paced
        movement.timingFunctions = [CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)]
        self.movement = movement
    }

    // set path for arc that will be moving
    func movingArc() -> UIBezierPath {
        let startDegree = 180.0
        let endDegree = 210.0
        let start = CGFloat(startDegree).toRadians()
        let end = CGFloat(endDegree).toRadians()

        let arcPath = UIBezierPath(arcCenter: CGPoint(x: 100, y: 100),
            radius: 100.0,
            startAngle: start,
            endAngle: end,
            clockwise: true)

        return arcPath
    }

    // draw shapeLayer for moving arc
    func movingArcLayer() -> CAShapeLayer {
        let color = UIColor.green

        let backgroundCircleLayer = CAShapeLayer()
        backgroundCircleLayer.path = movingArc().cgPath
        backgroundCircleLayer.fillColor = UIColor.clear.cgColor
        backgroundCircleLayer.strokeColor = color.cgColor
        backgroundCircleLayer.lineWidth = 4.0
        backgroundCircleLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
      //  backgroundCircleLayer.position = CGPoint(x: 0, y: 100)
      //  backgroundCircleLayer.transform = CATransform3DMakeTranslation(0, -100, 0)
      //  backgroundCircleLayer.transform = CATransform3DMakeRotation(CGFloat(-180).toRadians(), 0, 0, 1)

        self.layer.addSublayer(backgroundCircleLayer)

        return backgroundCircleLayer
    }

    // init for animation
    func initiateAnimation() {
        let layer = movingArcLayer()
        layer.add(self.movement, forKey: "Object Movement")
    }

    init() {
        super.init(frame: .zero)
        addAnimation()
        movingArcLayer()
        initiateAnimation()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

extension CGFloat {
    func toRadians() -> CGFloat {
        return self * CGFloat(Double.pi) / 180.0
    }
}

当绿色弧线代码看起来像这样时,它正在移动但在错误的位置,而不是沿着红色路径。 https://media.giphy.com/media/loSPpw2TPNNJyXSqcO/giphy.gif

当我将转换设置为:

  backgroundCircleLayer.position = CGPoint(x: 0, y: 100)
  backgroundCircleLayer.transform = CATransform3DMakeTranslation(0, -100, 0)

它在正确的路径上移动,但旋转错误: https://media.giphy.com/media/S85AIcXObCMfOhAjEe/giphy.gif

当我将转换设置为:

 backgroundCircleLayer.position = CGPoint(x: 100, y: 0)
 backgroundCircleLayer.transform = CATransform3DMakeTranslation(-100, 0, 0)

它在错误的路径上移动(偏移 90 度)但旋转是正确的。 https://media.giphy.com/media/mEEWv5i32TELTseyQG/giphy.gif

问题是,我应该如何正确设置图层变换,也许你知道解释 CoreAnimation 工作原理的好教程?

问题出在我为绿色弧线声明路径的方式上。以前它是一条单一的粗曲线。但是现在是二维平面了。

下面的工作代码:

import UIKit

class MovingRect: UIView {
    var path: UIBezierPath!
    var shapeLayer: CAShapeLayer!
    var movement: CAKeyframeAnimation!

    // set path for moving and draw it on screen
    func pathShapeLayer() -> UIBezierPath {
        let startDegree = 180.0
        let endDegree = 360.0
        let start = CGFloat(startDegree).toRadians()
        let end = CGFloat(endDegree).toRadians()

        path = UIBezierPath(arcCenter: CGPoint(x: 100, y: 100),
            radius: 100.0,
            startAngle: start,
            endAngle: end,
            clockwise: true)

        let pathShapeLayer = CAShapeLayer()
        pathShapeLayer.path = path.cgPath
        pathShapeLayer.fillColor = UIColor.clear.cgColor
        pathShapeLayer.lineWidth = CGFloat(4)
        pathShapeLayer.strokeColor = UIColor.red.cgColor
        pathShapeLayer.position = .init(x: 0, y: 0)

        self.layer.addSublayer(pathShapeLayer)

        return path
    }

    // set movemnet along path with rotation
    func addAnimation() {
        let movement = CAKeyframeAnimation(keyPath: "position")
        movement.path = pathShapeLayer().cgPath
        movement.duration = 2
        movement.repeatCount = 1
        movement.rotationMode = CAAnimationRotationMode.rotateAuto
        movement.calculationMode = CAAnimationCalculationMode.paced
        movement.timingFunctions = [CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)]
        self.movement = movement
    }
    
    func movingArcPath() -> UIBezierPath {
        let startDegree = 255.0
        let endDegree = 285.0
        let start = CGFloat(startDegree).toRadians()
        let end = CGFloat(endDegree).toRadians()

        path = UIBezierPath()
      
        path.addArc(withCenter: CGPoint(x: 100, y: 100), radius: 110, startAngle: start, endAngle: end, clockwise: true)
        path.addArc(withCenter: CGPoint(x: 100, y: 100), radius: 90, startAngle: end, endAngle: start, clockwise: false)
        path.close()
        
        return path
    }
    
    // draw shapeLayer for moving arc
    func movingArcLayer() -> CAShapeLayer {
        let color = UIColor.green

        let movingRectLayer = CAShapeLayer()
        movingRectLayer.path = movingArcPath().cgPath
        movingRectLayer.fillColor = color.cgColor
        movingRectLayer.strokeColor = color.cgColor
        movingRectLayer.lineWidth = 4.0
        movingRectLayer.transform = CATransform3DMakeTranslation(-100, 0, 0)
        movingRectLayer.position = CGPoint(x: 200, y: 100)
        movingRectLayer.anchorPoint = CGPoint(x: 200, y: 100)
     //   movingRectLayer.transform = CATransform3DMakeRotation(CGFloat(90).toRadians(), 0, 0, 1)
    
        self.layer.addSublayer(movingRectLayer)

        return movingRectLayer
    }

    // init for animation
    func initiateAnimation() {
        let layer = movingArcLayer()
        layer.add(self.movement, forKey: "Object Movement")
    }

    init() {
        super.init(frame: .zero)
        addAnimation()
        initiateAnimation()
      //  removeArcLayer()
        
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}