CAKeyFrameAnimation is not linear for values ​​greater than PI

I am having trouble understanding why the animation is not working as expected. What I am doing is:

  • Create a UIBezierPath with an arc to move the label along this path and animate the paths.

     //Start Point is -.pi /2 to let the Arc start at the top. //self.progress = Value between 0.0 and 1.0 let path : UIBezierPath = UIBezierPath.init(arcCenter: CGPoint.init(x: self.bounds.width * 0.5, y: self.bounds.height * 0.5), radius: self.bounds.width * 0.5, startAngle: -.pi / 2, endAngle: (2 * self.progress * .pi) - (.pi / 2), clockwise: true) return path 
  • Add this path to CAShapeLayer

     circlePathLayer.frame = bounds circlePathLayer.path = self.path.cgPath circlePathLayer.strokeStart = 0 circlePathLayer.strokeEnd = 1 
  • Animate the strokeEnd property with CABasicAnimation

     let animation = CABasicAnimation(keyPath: "strokeEnd") animation.repeatCount = HUGE animation.fromValue = 0.0 animation.toValue = 1.0 animation.duration = self.animationDuration animation.isRemovedOnCompletion = false animation.fillMode = kCAFillModeBoth 
  • Animate the position property of my label using CAKeyFrameAnimation

     let animationScore = CAKeyframeAnimation(keyPath: "position") //some things I tried to fix //animationScore.timingFunctions = [CAMediaTimingFunction(controlPoints: 0.250, 0.250, 0.750, 0.750)] //animationScore.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionLinear) animationScore.path = self.path.cgPath animationScore.duration = self.animationDuration animationScore.isRemovedOnCompletion = false animationScore.fillMode = kCAFillModeBoth animationScore.repeatCount = HUGE 
  • Add my animations to the layer and label

     self.circlePathLayer.add(animation, forKey: nil) self.scoreLabel.layer.add(animationScore, forKey: nil) 

My problem: For ProgressValues ​​greater than 0.75, my label does not move at linear speed. Values ​​greater than 0.75 mean that my arc is larger than PI. For values ​​less than 0.75, my animation works fine, and the label and stroke have the same speed and are on top of each other.

GIF: Gif showing animation

Please ignore 100% of the tag in this gif, my progress was 0.76.

You see that my mark slows down after three quarters of my circle.

Hope someone can help me. Many thanks

+6
source share
1 answer

An unnecessary complication is introduced in the keyframe animation. Just rotate the label around the center with the same duration as the dash layer animation:

enter image description here

(I apologize that my animation starts at the bottom, not the top, but I did not consider your question when I wrote the code, and now I'm too lazy to change it!)

So how is this done? These are three animations, all with the same duration:

  • The strokeEnd form level is strokeEnd like your animation.

  • A “hand” passing through the center of the circle, with the label as a sublayer at one end (so that the label appears in the radius of the circle). Hand makes rotation transformation animation.

  • The label makes the animation of the rotation transformation in the opposite direction. If he didn’t, he would spin with his superlayer. (Think about how the Ferris wheel works, your chair is at the end of the arm, but it remains vertical to the ground.)

This is all the animation code:

  let anim = CABasicAnimation(keyPath: "transform.rotation.z") anim.fromValue = 0 anim.toValue = 5 anim.duration = 10 self.arm.layer.add(anim, forKey:nil) let anim2 = CABasicAnimation(keyPath: "transform.rotation.z") anim2.fromValue = 0 anim2.toValue = -5 anim2.duration = 10 self.lab.layer.add(anim2, forKey:nil) let anim3 = CABasicAnimation(keyPath: "strokeEnd") anim3.fromValue = 0 anim3.toValue = 1 anim3.duration = 10 self.shape.add(anim3, forKey:nil) 
+4
source

Source: https://habr.com/ru/post/1014874/


All Articles