UIBezierPath 动画的奇怪问题

Strange problem with UIBezierPath animation

我想画月亮,然后给月亮的影子做动画。但是启动此代码后,我可以在动画线上看到一些故障:

动图:

为什么会这样?

游乐场代码here

更新一: 此函数创建的两条路径但角度不同(0 和 π/2*0.6):

func calculateMoonPath(for angle: CGFloat) -> UIBezierPath {
    let center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
    let radius = view.bounds.height/2
    
    let path = UIBezierPath(arcCenter: center,
                            radius: radius,
                            startAngle: -.pi/2,
                            endAngle: .pi/2,
                            clockwise: true)
    path.addArc(withCenter: .init(x: center.x - radius * tan(angle), y: center.y),
                radius: radius / CGFloat(cosf(Float(angle))),
                startAngle: .pi/2 - angle,
                endAngle: angle - .pi/2,
                clockwise: false
    )
    path.close()
    return path
}

根据我的经验,生成圆弧的代码会随着圆弧角度的变化在掩护下创建不同数量的三次贝塞尔曲线。

这改变了两条曲线中控制点的数量,并弄乱了动画。 (正如 David Rönnqvist 所说,如果开始和结束路径具有不同数量的控制点,则动画是未定义的。)

据我了解,一个完整的圆需要 4 条三次贝塞尔曲线才能完成。

创建始终使用 4 条三次贝塞尔曲线构建弧的 addArc 方法的变体并不难,无论弧角如何。这就是我的建议。

您可以将弧分成 4 段(使用 4 次连续调用 addArc(withCenter:...) 并使用不同的开始和结束角度,以便它们结合起来形成您想要的完整弧。每个弧都应该足够短长度为单个贝塞尔曲线,因此您应该为开始和结束组合曲线获得相同数量的控制点。

如果您像这样重写 calculateMoonPath 函数:

func calculateMoonPath(for angle: CGFloat) -> UIBezierPath {
    let center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
    let radius = view.bounds.height/2

    let path = UIBezierPath(arcCenter: center,
                            radius: radius,
                            startAngle: -.pi/2,
                            endAngle: .pi/2,
                            clockwise: true)
    let startAngle = .pi/2 - angle
    let endAngle = angle - .pi/2
    let delta = (endAngle - startAngle) / 4
    for index in 0...3 {
        let thisStart = startAngle + delta * CGFloat(index)
        let thisEnd = startAngle + delta * CGFloat(index + 1)
        path.addArc(withCenter: .init(x: center.x - radius * tan(angle), y: center.y),
                    radius: radius / CGFloat(cosf(Float(angle))),
                    startAngle: thisStart,
                    endAngle: thisEnd,
                    clockwise: false
        )
    }
    path.close()
    return path
}

产生以下结果:

你做这两行的方式,

简单来说,两个控制点!

每端一个。

仅此而已。不要尝试使用圆弧。

这里...

https://www.desmos.com/calculator/cahqdxeshd