CAKeyFrameAnimation не является линейным для значений, превышающих PI

У меня возникли проблемы с пониманием, почему анимация не работает так, как ожидалось. Что я делаю, так это:

  1. Создать UIBezierPath с помощью дуги, чтобы переместить метку вдоль этого пути и анимировать обводку путей.

    //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
    
  2. Добавить этот путь к CAShapeLayer

    circlePathLayer.frame = bounds
    circlePathLayer.path = self.path.cgPath
    circlePathLayer.strokeStart = 0
    circlePathLayer.strokeEnd = 1
    
  3. Анимируйте свойство strokeEnd с помощью 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
    
  4. Анимируйте свойство position моего ярлыка с помощью 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
    
  5. Добавить мои анимации в слой и метку

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

Моя проблема: для значений ProgressValues ​​больше 0,75 моя метка не движется с линейной скоростью. Значения больше 0,75 означают, что моя дуга больше, чем PI. При значениях менее 0,75 моя анимация работает нормально, а метки и штрихи имеют одинаковую скорость и находятся друг над другом.

GIF: Gif показывает анимацию

Пожалуйста, игнорируйте 100% в ярлыке в этом gif, мой прогресс был на уровне 0,76.

Вы видите, что мой ярлык замедляется после трех четвертей моего круга.

Я надеюсь, что кто-то может помочь мне. Большое спасибо

1 ответ

Решение

Анимация ключевого кадра вносит ненужные сложности. Просто поверните метку вокруг центра на ту же продолжительность, что и анимация обводки слоя формы:

введите описание изображения здесь

(Я извиняюсь, что моя анимация начинается снизу, а не сверху, но я не смотрел на ваш вопрос, когда писал код, и теперь мне лень его менять!)

Итак, как это сделать? Это три анимации, все с одинаковой продолжительностью:

  • Слой формы strokeEndнравится твоя анимация.

  • "Рука", проходящая через центр круга, с меткой в ​​качестве подслоя на одном конце (так что метка появляется в радиусе круга). Рука делает анимацию вращения вращения.

  • Метка выполняет вращение с преобразованием анимации в обратном направлении. Если бы он этого не сделал, он бы вращался вместе со своим суперслоем. (Подумайте о том, как работает колесо обозрения; ваше кресло находится на конце руки, но оно остается в вертикальном положении относительно земли.)

Это весь код анимации:

    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)
Другие вопросы по тегам