当 CAAnimationGroup 的执行完成时,它会回到初始动画序列,并带有抽搐效果

Posted

技术标签:

【中文标题】当 CAAnimationGroup 的执行完成时,它会回到初始动画序列,并带有抽搐效果【英文标题】:When CAAnimationGroup's execution is completed, it goes back to initial animation sequence with a jerking effect 【发布时间】:2020-08-08 08:57:11 【问题描述】:

在这里,我正在创建一个消失的圆形图层动画。我的代码如下:

import UIKit

class VanishingLoader: UIView 
    
    private let loaderLayer = CAShapeLayer()
    private var layerPaths = [UIBezierPath]()
   
    private let animationframeDuration = 0.25
    private var lastFrameDuration = 0.0
    weak var parentView: UIView?
       
    var parenViewCenter: CGPoint?
      if let frame = parentView?.bounds
         return CGPoint(x: frame.midX, y: frame.midY)
      
      return nil
    
    let animationGroup = CAAnimationGroup()
    
    required init?(coder: NSCoder) 
        super.init(coder: coder)
    
    
    init(onView view: UIView) 
        super.init(frame: view.frame)
        parentView = view
        layerPaths = preparedPathList
        if layerPaths.count > 0 
            let path = layerPaths[0]
            addVanishingStrokeLayer(path, withStrokeColor: UIColor.systemGreen, andStrokeWidth: nil)
        
    
 
    private func addVanishingStrokeLayer(_ path: UIBezierPath, withStrokeColor strokeColor:UIColor?, andStrokeWidth strokeWidth: CGFloat?)
        
        let initialPath = layerPaths[0].cgPath
        loaderLayer.path = initialPath
        loaderLayer.fillColor = UIColor.clear.cgColor
      
        if let loaderLayerColor = strokeColor
            loaderLayer.strokeColor = loaderLayerColor.cgColor
        else
           loaderLayer.strokeColor = defaultStrokeColor.cgColor
        
       
        if let lineWidth = strokeWidth
           loaderLayer.lineWidth = lineWidth
        else
           loaderLayer.lineWidth = 5.0
        
        loaderLayer.add(vanishingAnimationGroup(), forKey: "vanishingGroupAnimation")
        self.layer.addSublayer(loaderLayer)
    


    var preparedPathList: [UIBezierPath]
        var pathList = [UIBezierPath]()
        var multiplier : CGFloat = 0.3
        for _ in 0..<8 
          if let path = getPathForLayer(withMultiplier: multiplier)
              pathList.append(path)
              multiplier += 0.10
          
        
        return pathList
    
    
    private func getPathForLayer(withMultiplier multiplier: CGFloat = 1.0)->UIBezierPath?
         let radius = 5.0
         let path = UIBezierPath(arcCenter: .zero , radius: radius, startAngle: 0, endAngle: 4 * CGFloat.pi, clockwise: true)
         return path
    
    
    
    private func prepareForwardAnimationFrame(forPathCount count: Int)->CASpringAnimation
       
        let initialPath = layerPaths[count]
        let finalPath = layerPaths[count + 1]
        
        let pathAnimation = CASpringAnimation(keyPath: "path")
        pathAnimation.fromValue = initialPath.cgPath
        pathAnimation.toValue = finalPath.cgPath
        pathAnimation.initialVelocity = 1
        pathAnimation.damping = 5
        pathAnimation.beginTime = lastFrameDuration
        pathAnimation.duration = animationframeDuration
        
        
        return pathAnimation
    

    
    private func vanishingAnimationGroup()->CAAnimationGroup

        var animationPaths = [CASpringAnimation]()

        for count in 0..<(layerPaths.count - 1)
            let anim = prepareForwardAnimationFrame(forPathCount: count)
            animationPaths.append(anim)
            lastFrameDuration = anim.beginTime + animationframeDuration
        

        animationGroup.animations = animationPaths
        animationGroup.duration = (Double(animationPaths.count) * animationframeDuration)
        animationGroup.repeatCount = .infinity
        animationGroup.fillMode = .forwards
        animationGroup.isRemovedOnCompletion = false
        return animationGroup
    
    
    public func hide()
         self.removeFromSuperview()
     
    
     public func show()
         if let view = parentView
           view.addSubview(self)
         
     

从 ViewController 中,必须添加以下两行才能看到效果。

let loader = VanishingLoader(onView: placeHolderImageView)
loader.show()

我面临的问题是,当动画序列结束时,它会回到初始路径,这是七条路径中最小的一条,并且会产生突然的抽搐效果。请帮帮我。

【问题讨论】:

【参考方案1】:

首先,这应该是关键帧动画,而不是分组动画。其次,从 last 到 first 的变化没有动画,因为 you 没有动画。添加另一个执行缺失动画的关键帧。

我把你的动画改写成关键帧动画了,到现在也有这个,但是我觉得不是你想要的,所以请说清楚应该是什么效果:

【讨论】:

你能提供一些代码吗?我已经根据您的建议更新了我的代码,但它根本不起作用。现在它甚至没有动画。请查看我更新的帖子。 因为这不是您制作关键帧动画的方式。关键时间是分数 我添加了 keyTimes 数组,其中包含我希望每个关键帧的持续时间的分数。依然没有。用你的建议更新了我的答案.. 为了突出重点,我发布了主要部分。由于您还不够清楚,我正在用整个自定义类更新我的问题。 让我们continue this discussion in chat。

以上是关于当 CAAnimationGroup 的执行完成时,它会回到初始动画序列,并带有抽搐效果的主要内容,如果未能解决你的问题,请参考以下文章

CAAnimationGroup 运行太快

CAAnimationGroup 与 CAKeyframeAnimation 和 CABasicAnimation

使用 CAAnimationGroup 对两个核心动画进行分组会导致一个 CABasicAnimation 无法运行

iOS 旋转动画在 CAAnimationGroup 中发生不顺畅

CAAnimationGroup 中的 CAAnimation 委托

具有不同界面元素的 CAAnimationGroup