如何在Core Animation中访问计算出的动画小值?

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

我正在为我的动画使用下面的代码:

let animation = CABasicAnimation()
animation.keyPath = "position.x"
animation.fromValue = 0
animation.toValue = 300
animation.timingFunction = .init(name: .easeInEaseOut)
animation.duration = 2


nsView.layer?.add(animation, forKey: "basic")

如果您查看在 2 秒内看到的代码,我们正在从 0 变为 300,我想访问介于两者之间的计算值,例如:

0.0, 0.2, 0.3, ..., 300.0
CABasicAnimation 那么我该怎么做呢?

swift cocoa core-animation
1个回答
0
投票

CAMediaTimingFunction
s 只是三次贝塞尔曲线。正如您从维基百科页面中看到的那样,曲线由四个控制点参数化定义,我们可以使用
getControlPoint
.

之后,我们可以根据参数t得到曲线y坐标的方程。但现在不是时候。相反,曲线的 x 坐标是动画的时间。所以我们需要用 x 坐标来表示 t。我们已经可以使用控制点(参见维基百科上的方程)用三次方程用 t 表示 x,所以我们只需要求解方程。 告诉你从哪里开始。

最后,我们只需要代入x,也就是时间,得到参数t,然后代入y坐标的方程。

综上所述,我们可以编写这样的函数:

func getCallableFunction(fromTimingFunction function: CAMediaTimingFunction) -> (Float) -> Float {
    // Each *pair* of elements in cps store a control point.
    // the elements at even indices store the x coordinates
    // the elements at odd indices store the y coordinates
    var cps = Array(repeating: Float(0), count: 8)
    cps.withUnsafeMutableBufferPointer { pointer in
        for i in 0..<4 {
            function.getControlPoint(at: i, values: &pointer[i * 2])
        }
    }

    return { x in
        // assuming the curve doesn't do weird things like loop around on itself, this only has one real root
        // coefficients got from https://pomax.github.io/bezierinfo/#yforx
        // implementation of cubicSolve from https://gist.github.com/kieranb662/b85aada0b1c03a06ad2f83a0291d7243
        let t = Float(cubicSolve(
            a: Double(-cps[0] + cps[2] * 3 - cps[4] * 3 + cps[6]),
            b: Double(cps[0] * 3 - cps[2] * 6 + cps[4] * 3),
            c: Double(-cps[0] * 3 + cps[2] * 3),
            d: Double(cps[0] - x)
        ).first(where: \.isReal)!.real)
        
        // getting y from t, see equation on Wikipedia
        return powf(1 - t, 3) * cps[1] +
                powf(1 - t, 2) * t * cps[3] * 3 +
                (1 - t) * t * t * cps[5] * 3 +
                t * t * t * cps[7]
    }
}

请注意,我使用了 this gist 中的立方求解器。随意使用另一个更好的算法。

default
计时功能的用法示例:

let function = getCallableFunction(fromTimingFunction: .init(name: .default))
for i in stride(from: Float(0), through: 1, by: 0.05) {
    let result = function(i)
    print(result) // this prints a result between 0 and 1
}

在图表上绘制结果如下所示:

这与

default
的文档中显示的图形非常相似。

要将其应用于特定动画,只需线性缩放函数的时间和输出。例如,对于持续时间为 2 且动画在 0 到 300 之间的动画:

let result = function(time / 2) * 300
© www.soinside.com 2019 - 2024. All rights reserved.