15

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

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

但是,Apple 的实现会产生一个标量值(您可以在此图中看到他们绘制 x(t) 与 t 的关系图:http: //developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Animation_Types_Timing/Articles/ Timing.html#//apple_ref/doc/uid/TP40006670-SW1 )

那么苹果是不是简单地忽略了结果的 y 坐标而只处理 x 呢?这看起来很奇怪,因为这样您就不需要传递控制点而是控制标量,因为 y 根本不会影响结果。

4

4 回答 4

13

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

我自己需要它,所以构建了一个类,其接口和功能CAMediaTimingFunction与所需的-valueForX:方法完全相同;用法示例:

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

您可以创建缓入、缓出、缓入缓出或任何可以用三次贝塞尔曲线描述的曲线。实现数学基于 WebCore (WebKit),这大概也是 CoreAnimation 在后台使用的。

享受吧,拉斐尔

于 2013-09-29T22:23:48.527 回答
5

不幸的是,Core Animation 没有公开其动画时间的内部计算模型。然而,对我来说真正有效的是使用 Core Animation 来完成这项工作!

  1. 创建一个CALayer作为评估者
  2. 将其框架设置为((0.0, 0.0), (1.0, 1.0))
  3. 设置isHiddentrue
  4. 设置speed0.0
  5. 将此层添加到某个容器层
  6. 当你想评估 anyCAMediaTimingFunction时,创建一个参考动画:

    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.0和之间):1.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
    }
    

基本上,您使用 Core Animation 为不可见层运行动画。但是图层speed0.0,所以它根本不会进行动画。使用timeOffset,我们可以手动调整动画的当前位置,然后获取其表示层的 x 位置。这表示由动画驱动的该属性的当前感知值。

这有点不合常规,但并没有什么骇人听闻的地方。它尽可能忠实地表示 a 的输出值,CAMediaTimingFunction因为 Core Animation 实际上正在使用它。

唯一需要注意的是表示层是屏幕上显示的值的近似值。Core Animation 不保证它们的准确性,但在我使用 Core Animation 的这些年里,我从未见过它不准确。不过,如果您的应用程序需要绝对准确度,那么这种技术可能不是最好的。

于 2017-04-01T20:28:37.057 回答
4

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

苹果在这里混合了坐标系,这造成了一些混乱。

x(t)在示例图中,表示沿某个路径的标量级数,而不是物理坐标。

CAMediaTimingFunction 中使用的控制点用于描述此进程,而不是几何点。更令人困惑x的是,控制点实际上映射到t绘图上,而y控制点实际上映射到x(t)绘图上。

以该图kCAMediaTimingFunctionEaseInEaseOut为例,该图将大致由控制点 (0,0)、(0.5,0)、(0.5,1)、(1,1) 来描述。

于 2012-04-11T02:09:01.783 回答
1

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

更冗长,他们使用计算参数t(与时间无关)给定一个x实际包含时间值的值。然后他们使用Newton-Raphson 方法附加示例说明)。由于它可能会失败,因此他们反复使用二分法(“分而治之”)。solveCurveX()在方法中看到这个。

获得参数后t(鉴于三次贝塞尔曲线是参数化定义的,这是必要的),他们只需使用它来计算y. solve()在方法中看到这个。

顺便说一句,卡布奇诺的忠实粉丝,仍然希望发布 Atlas :-)


2020 年 1 月更新:我在 2012 年实现核心动画 API 时就这样做了。随意参考它。

于 2012-07-15T21:36:59.590 回答