UIBezierPath 核心动画反转描边开始和描边结束

问题描述 投票:0回答:1

我正在尝试创建一个不定加载器,其中一个栏从左向右移动。它有点像您在 Google Apps 中看到的旋转器,但后来是一个栏。这篇博客是一篇很棒的文章,展示了如何使用 UIBezierPaths 和 CABasicAnimation 创建这样的旋转器:

http://blog.matthewcheok.com/design-teardown-spinning-indicator/

但就我而言,我想对酒吧做同样的事情。因此,有一条从左到右的线,就像在微调器中一样,我还以较小的延迟对中风开始进行动画处理,使其看起来沿着线移动并在最后赶上它。 当它到达末尾时,我希望它从右向左返回,等等。 所有这些都使用easeIn 和easeOut。而且我不希望线条到达末端时不可见,一小部分应该始终保持可见。

为了让我的解释更清楚,这就是我想要的:

我知道我似乎已经有了我想要的东西,但我使用丑陋的延迟代码创建了这个 gif,但它有一半时间无法正常工作。

因此,继续,不能使用 CABasicAnimation 的自动反转属性,因为我对中风结束和中风开始都进行了动画处理,所以我有两个想法:

  1. 仅使用一层,并在线条到达左端或右端时立即更新 bezierPath。
  2. 使用两个动画层,并在线条到达左端或右端时将其中一个替换为另一个。

现在,我已经走了很远了,但棘手的部分是我必须切换图层。无论我尝试什么,我总是看到闪烁。我知道我必须使用表示层并在添加动画组之前或之后设置正确的值。

我使用 CATransaction.setCompletionBlock 来了解何时到达左侧或右侧部分,然后转向另一个方向。

以下是我编写的代码的一些部分:

UIBezierPaths:

    let start:CGPoint = .init(x: 0, y: 0)
    let end:CGPoint = .init(x: frame.width, y: 0)
        
    let pathLeft = UIBezierPath()
    pathLeft.move(to: start)
    pathLeft.addLine(to: end)
    self.barLayerLeft.path = pathLeft.cgPath
       
    let pathRight = UIBezierPath()
    pathRight.move(to: end)
    pathRight.addLine(to: start)
    self.barLayerRight.path = pathRight.cgPath

设置描边开始和描边结束动画和一组

    let strokeStartAnimation: CAAnimation = {
        let animation = CABasicAnimation(keyPath: "strokeStart")
        animation.fromValue = 0
        animation.toValue = 0.99
        animation.beginTime = 0.3
        animation.duration = 1
        animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        return animation
    }()
    
    let strokeEndAnimation: CAAnimation = {
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1
        animation.beginTime = 0
        animation.duration = 1.3
        animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        return animation
    }()

组和图层

    let groupedAnimation:CAAnimationGroup = CAAnimationGroup()
    groupedAnimation.duration = 1.4
    groupedAnimation.animations = [self.strokeStartAnimation, self.strokeEndAnimation]
        
    self.barLayerLeft.lineWidth = 4
    self.barLayerLeft.lineCap = .round
    self.barLayerLeft.strokeColor = AppStyler.mainColor.cgColor
    self.layer.addSublayer(self.barLayerLeft)
      
    self.barLayerRight.lineWidth = 4
    self.barLayerRight.lineCap = .round
    self.barLayerRight.strokeColor = AppStyler.mainColor.cgColor
    self.layer.addSublayer(self.barLayerRight)

向左动画(包括设置最终位置,以便动画线将准确地保持在动画完成时的位置,并删除从右到左的栏)

    self.barLayerLeft.strokeEnd = 1
    self.barLayerLeft.strokeStart = 0.01
    self.barLayerRight.strokeEnd = 1
    self.barLayerRight.strokeStart = 0
    
    self.barLayerLeft.add(self.groupedAnimation, forKey: "groupLeft")
    CATransaction.setCompletionBlock{ [weak self] in
        self.animateRight()        
    }

向右动画(包括上面的位置)

    self.barLayerRight.strokeEnd = 1
    self.barLayerRight.strokeStart = 0.01        
    self.barLayerLeft.strokeEnd = 1
    self.barLayerLeft.strokeStart = 0
        
    self.barLayerRight.add(self.groupedAnimation, forKey: "groupRight")
    CATransaction.setCompletionBlock{ [weak self] in
        self.animateLeft()        
    }

我已经尝试使用“CATransaction.setDisableActions”来切换图层,而不会看到这样的闪烁:

    CATransaction.begin()
    CATransaction.setDisableActions(true)
    self.barLayerLeft.strokeEnd = 0.01
    self.barLayerLeft.strokeStart = 0
    self.barLayerRight.strokeEnd = 1
    self.barLayerRight.strokeStart = 0
    CATransaction.commit()

但我无法让它工作......

所以,我的主要问题是。我上面的想法真的可以不闪烁吗?或者我需要完全使用其他东西......?有没有更简单/更好的方法来实现我想要的?

顺便说一句,如果有什么不清楚或者我需要发布更多代码,请告诉我!

swift animation uibezierpath cabasicanimation caanimation
1个回答
0
投票

我会使用遮罩和单个移动层来解决这个问题:

let container = CALayer()
container.frame = CGRect(x: 10, y: 10, width: 200, height: 10)
container.borderColor = UIColor.black.cgColor
container.borderWidth = 1
container.cornerRadius = 5
container.masksToBounds = true
view.layer.addSublayer(container)
let bar = CALayer()
bar.frame = container.bounds
bar.anchorPoint = CGPoint(x: 0, y: 0.5)
bar.position = CGPoint(x: -195, y: 5)
bar.backgroundColor = UIColor.blue.cgColor
container.addSublayer(bar)
let animation = CABasicAnimation(keyPath: "position.x")
animation.fromValue = bar.position.x
animation.toValue = 195
animation.autoreverses = true
animation.repeatCount = .infinity
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
animation.duration = 2
bar.add(animation, forKey: nil)

看起来像这样,但没那么可怕,因为它不是 GIF:

在工作结束时暂停会更简单,因为它是单个动画。

© www.soinside.com 2019 - 2024. All rights reserved.