使用CAMediaTimingFunction计算时间(t)的值

时间:2012-04-11 01:12:16

标签: core-animation bezier

在Cocoa / Touch中,CAMediaTimingFunction表示四个控制点,指定计时函数的三次贝塞尔曲线。对于我正在编写的应用程序,我希望能够在任意时间t(0 - > 1)提取所述贝塞尔曲线的结果。令我困惑的是,当我查看如何do this时,结果应该是一个点,而不是标量:

B(t) = (1 - t) ^ 3 * P0 + 3 * (1 - t) ^ 2 * t * P1 + 3 * (1 - t) * t ^ 2 * P2 + t ^ 2 * P3

然而,Apple的实现会产生一个标量值(你可以在这个图上看到他们绘制的x(t)vs t:http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Animation_Types_Timing/Articles/Timing.html#//apple_ref/doc/uid/TP40006670-SW1

那么Apple是否只是忽略结果的y坐标而只处理x?这看起来很奇怪,因为那时你不需要传递控制点,而是控制标量,因为y不会影响结果。

4 个答案:

答案 0 :(得分:11)

CoreAnimation的CAMediaTimingFunction可以满足您的需要,但不会为给定的'x'提供'y'用于多功能(动画)使用,而只是将解决的值不透明地提供给引擎盖下的动画系统。

我自己需要它built a class with the interface and capabilities exactly like CAMediaTimingFunction but with the needed -valueForX: method;用法示例:

RSTimingFunction *heavyEaseInTimingFunction = [RSTimingFunction timingFunctionWithControlPoint1:CGPointMake(0.8, 0.0) controlPoint2:CGPointMake(1.0, 1.0)];
CGFloat visualProgress = [heavyEaseInTimingFunction valueForX:progress];

您可以使用a cubic Bézier curve创建轻松,轻松,轻松自如或任何可以描述的曲线。实现数学基于WebCore(WebKit),这可能是CoreAnimation在引擎盖下使用的。

享受, 圣拉斐尔

答案 1 :(得分:4)

注意:我不是CoreAnimation的专家。这只是我通过阅读您链接的文档的理解。

Apple正在混合坐标系统,这造成了一些混乱。

示例图中的

x(t)表示沿某条路径的标量进度,而不是物理坐标。

CAMediaTimingFunction中使用的控制点用于描述此进程,而不是几何点。为了增加混淆,控制点中的x实际上映射到图上的t,并且控制点中的y映射到图上的x(t)

kCAMediaTimingFunctionEaseInEaseOut的情节为例,该图将由控制点(0,0),(0.5,0),(0.5,1),(1,1)粗略描述。

答案 2 :(得分:2)

这很不幸,但Core Animation没有为其动画时序公开其内部计算模型。然而,对我来说非常有用的是使用Core Animation来完成这项工作!

  1. 创建CALayer以充当评估者
  2. 将其框架设置为((0.0, 0.0), (1.0, 1.0))
  3. isHidden设为true
  4. speed设为0.0
  5. 将此图层添加到某个容器图层
  6. 如果要评估任何CAMediaTimingFunction,请创建参考动画:

    let basicAnimation = CABasicAnimation(keyPath: "bounds.origin.x")
    basicAnimation.duration = 1.0
    basicAnimation.timingFunction = timingFunction
    basicAnimation.fromValue = 0.0
    basicAnimation.toValue = containerLayer.bounds.width
    
    referenceLayer.add(basicAnimation, forKey: "evaluatorAnimation")
    
  7. 将参考图层的timeOffset设置为您要评估的任何规范化输入值(即0.01.0之间):

    referenceLayer.timeOffset = 0.3    // 30% into the animation
    
  8. 询问参考图层的表示层,并获取其当前边界的原点x值:

    if let presentationLayer = referenceLayer.presentation() as CALayer? {
        let evaluatedValue = presentationLayer.bounds.origin.x / containerLayer.bounds.width
    }
    
  9. 基本上,您正在使用Core Animation为不可见的图层运行动画。但是图层的speed0.0,因此它根本不会进展动画。使用timeOffset,我们可以手动调整动画的当前位置,然后获取其表示层的x位置。这表示由动画驱动的该属性的当前感知值。

    这有点不同寻常,但没有任何关于它的hacky。它是忠实的CAMediaTimingFunction输出值的表示,因为Core Animation实际上正在使用它。

    唯一需要注意的是,表现层与屏幕上显示的值接近近似。核心动画并不保证其准确性,但在我使用核心动画的这些年里,我从未见过它是不准确的。尽管如此,如果您的应用程序需要绝对准确,那么这种技术可能不是最好的。

答案 3 :(得分:1)

WebKit源代码中的最佳提示可能是UnitBezier.h。事实上,我猜Apple在Core Animation中使用相同的代码。

更长,他们使用计算参数t(与时间无关)给定值x实际包含时间值。然后他们使用Newton-Raphson methodadditional explanation with examples)。由于它可能会失败,因此它们会再次使用二分法(“分而治之”)。请参阅solveCurveX()方法。

在获得参数t之后(鉴于参数定义了三次贝塞尔曲线,这是必要的),他们只是用它来计算y。请参阅solve()方法。

顺便说一句,卡布奇诺的忠实粉丝,仍然希望释放阿特拉斯: - )