使用 CASpringAnimation 动画化 CAShapeLayer 路径

Animate CAShapeLayer path with CASpringAnimation

我有一个自定义 UIView,我在其中添加了一个 CAShapeLayer 作为视图层的直接子层:

private let arcShapeLayer = CAShapeLayer()

CAShapeLayerawakeFromNib 中声明了以下属性(边框是为了使动画更容易可视化):

arcShapeLayer.lineWidth = 2.0
arcShapeLayer.strokeColor = UIColor.black.cgColor
arcShapeLayer.fillColor = UIColor.lightGray.cgColor
arcShapeLayer.masksToBounds = false
layer.masksToBounds = false
layer.addSublayer(arcShapeLayer)

我在layoutSubviews中声明arcShapeLayer的框架:

arcShapeLayer.frame = layer.bounds

然后我有以下功能,我通过一定百分比(从ViewController)以在一些拖动后实现弧形效果。简单的说我在层的底部加了一个quadCurve

func configureArcPath(curvePercentage: CGFloat) -> CGPath {

    let path = UIBezierPath()
    path.move(to: CGPoint.zero)
    path.addLine(to:
        CGPoint(
            x: arcShapeLayer.bounds.minX,
            y: arcShapeLayer.bounds.maxY
        )
    )

    path.addQuadCurve(
        to:
        CGPoint(
            x: arcShapeLayer.bounds.maxX,
            y: arcShapeLayer.bounds.maxY
        ),
        controlPoint:
        CGPoint(
            x: arcShapeLayer.bounds.midX,
            y: arcShapeLayer.bounds.maxY + arcShapeLayer.bounds.maxY * (curvePercentage * 0.4)
        )
    )

    path.addLine(to:
        CGPoint(
            x: arcShapeLayer.bounds.maxX,
            y: arcShapeLayer.bounds.minY
        )
    )
    path.close()

    return path.cgPath

}

然后我尝试用下面的代码用spring效果弧制作动画:

func animateArcPath() {

    let springAnimation = CASpringAnimation(keyPath: "path")
    springAnimation.initialVelocity = 10
    springAnimation.mass = 10
    springAnimation.duration = springAnimation.settlingDuration
    springAnimation.fromValue = arcShapeLayer.path
    springAnimation.toValue = configureArcPath(curvePercentage: 0.0)
    springAnimation.fillMode = .both
    arcShapeLayer.add(springAnimation, forKey: nil)

    arcShapeLayer.path = configureArcPath(curvePercentage: 0.0)

}

正如您在视频中看到的那样,问题是 电弧永远不会超过 。虽然它在原始位置和静止位置之间摆动,但从未达到 spring 效果。

我在这里错过了什么?

我试过这个,我可以报告你是完全正确的。它与零位钳位无关。可见动画夹在 两个 极端。

假设您提供了一个很大的质量值,这样超调应该会越过凸弓的初始位置 在其 return 旅程中 回到起点.然后我们看到的是动画将 both 夹在原始位置 and 上,您尝试将其设置为最小值。当 spring 在它们之间摆动时,我们看到动画在一个极端和另一个极端之间发生,但当它达到最大值或最小值时,它只是固定在那里,直到它有时间摆动到最大程度并且 return.

我不得不得出结论,CAShapeLayer path 不喜欢 springing 动画。 CAShapeLayer path spring animation not 'overshooting'

提出了同样的观点

我能够通过链接正常的基本动画来模拟您可能想要的那种外观:

这是我使用的代码(基于你自己的代码):

@IBAction func animateArcPath() {
    self.animate(to:-1, in:0.5, from:arcShapeLayer.path!)
}
func animate(to arc:CGFloat, in time:Double, from current:CGPath) {
    let goal = configureArcPath(curvePercentage: arc)
    let anim = CABasicAnimation(keyPath: "path")
    anim.duration = time
    anim.fromValue = current
    anim.toValue = goal
    anim.delegate = self
    anim.setValue(arc, forKey: "arc")
    anim.setValue(time, forKey: "time")
    anim.setValue(goal, forKey: "pathh")
    arcShapeLayer.add(anim, forKey: nil)
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
    if let arc = anim.value(forKey:"arc") as? CGFloat,
        let time = anim.value(forKey:"time") as? Double,
        let p = anim.value(forKey:"pathh") {
        if time < 0.05 {
            return
        }
        self.animate(to:arc*(-0.5), in:time*0.5, from:p as! CGPath)
    }
}