Использование CAMediaTimingFunction для вычисления значения в момент времени (t)

В 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 приводит к скалярному значению (вы можете видеть на этом графике график h (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 не повлияет на результат.

Ответы

Ответ 1

Примечание. Я не эксперт по CoreAnimation. Это просто мое понимание от чтения документов, которые вы связали.

Apple смешивает системы координат здесь, что создает некоторую путаницу.

x(t) в примерах графика представляет скалярную прогрессию вдоль некоторого пути, а не физическую координату.

Контрольные точки, используемые в CAMediaTimingFunction, используются для описания этой прогрессии, а не для геометрических точек. Чтобы добавить к путанице, x в контрольных точках фактически отображает на t на графиках и y в контрольных точках до x(t) на графиках.

Чтобы взять график для kCAMediaTimingFunctionEaseInEaseOut в качестве примера, этот график будет примерно описан контрольными точками (0,0), (0,5,0), (0,5,1), (1,1).

Ответ 2

CoreAnimation CAMediaTimingFunction делает то, что вы хотите, но не обнаруживает получение "y" для заданного "x" для универсального использования (анимации), а скорее просто подает разрешенные значения незаметно в анимационную систему под капотом.

Мне это было нужно, поэтому построил класс с интерфейсом и возможностями в точности как 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 под капотом.

Enjoy, Рафаэль

Ответ 3

Лучшим намеком, вероятно, будет UnitBezier.h в исходном коде WebKit. На самом деле, я думаю, Apple использует тот же код внутри Core Animation.

Более длинные, они используют параметр расчета t (который не имеет никакого отношения к времени), учитывая значение x, которое фактически содержит значение времени. Затем они используют метод Ньютона-Рафсона (дополнительное объяснение с примерами). Поскольку это может потерпеть неудачу, они переходят к использованию бисекции ( "разделяй и властвуй" ). См. Это в методе solveCurveX().

После получения параметра t (что необходимо, учитывая, что кубические Beziers параметрически определены), они просто используют его для вычисления y. См. Это в методе solve().

Кстати, большой поклонник Cappuccino и все еще надеется на выпуск Atlas: -)

Ответ 4

Несчастливо, но Core Animation не раскрывает свою внутреннюю вычислительную модель для ее анимации. Тем не менее, для меня было очень полезно использовать Core Animation для выполнения этой работы!

  • Создайте CALayer для работы в качестве оценщика.
  • Установите рамку на ((0.0, 0.0), (1.0, 1.0))
  • Установите isHidden в true
  • Установите speed в 0.0
  • Добавьте этот слой в некоторый слой контейнера.
  • Если вы хотите оценить любой 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")
    
  • Установите опорный уровень timeOffset на любое нормализованное входное значение (т.е. между 0.0 и 1.0), которое вы хотите оценить:

    referenceLayer.timeOffset = 0.3    // 30% into the animation
    
  • Попросите презентационный слой опорного слоя, и получить его текущее значение оценки происхождения х:

    if let presentationLayer = referenceLayer.presentation() as CALayer? {
        let evaluatedValue = presentationLayer.bounds.origin.x / containerLayer.bounds.width
    }
    

В основном, вы используете Core Animation для запуска анимации для невидимого слоя. Но слой speed равен 0.0, поэтому он вообще не будет продвигать анимацию. Используя timeOffset, мы можем вручную отрегулировать текущую позицию анимации, а затем получить ее позицию x слоя представления. Это представляет текущую воспринимаемую ценность этого свойства, обусловленную анимацией.

Это немного нетрадиционно, но в нем нет ничего глупого. Это как верное представление выходного значения CAMediaTimingFunction, как вы можете получить, потому что Core Animation фактически использует его.

Единственное, о чем нужно знать, это то, что слои представления представляют собой приблизительные приближения значений, представленных на экране. Core Animation не дает никаких гарантий относительно их точности, но за все годы использования Core Animation я никогда не видел, чтобы это было неточно. Тем не менее, если ваше приложение требует абсолютной точности, возможно, этот метод может быть не лучшим.