Как я могу создать iOS CAAnimation и определить каждое интерполированное значение самостоятельно? - PullRequest
0 голосов
/ 11 января 2019

Я хочу оживить путь CAShapeLayer. Однако этот путь создается с использованием многих дуг. Количество дуг одинаково в начальном и конечном значении пути. Однако, поскольку дуги имеют разную длину, это означает, что в реальных путях используется разное количество контрольных точек Безье. Это означает, что анимация не предсказуемая и очень глючная *.

Мое решение пока состоит в том, чтобы создать CAKeyFrameAnimation и добавить очень большое количество ключевых кадров. Он работает только с очень большим количеством ключевых кадров в секунду. (около 200 до 300). Все, что ниже, и глюки * начинают появляться.

Этот подход очень грубый, поскольку успех зависит от фактической частоты кадров на реальном устройстве и создает больше экземпляров ключевых кадров, чем фактически требуется для анимации, тратя впустую память. Итак, мой вопрос: есть ли способ создать анимацию, где я должен как-то создать каждое значение? Я придумываю некоторый подход, основанный на делегатах, где меня просят значение, основанное на ключевом времени Я не знаю, как лучше всего подойти к моей проблеме.

* Примечание : Глюки, о которых я говорю, - это странно выглядящие пути, которые появляются только для одного кадра.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

UIBezierPath и его базовый базовый класс CGPath действительно используют переменное число контрольных точек при создании дуги в зависимости от угла дуги.

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

Я предлагаю использовать сплайны Катмулла-Рома для создания ваших дуг. Сплайн Кэтмулла-Рома - это другой вид кубической кривой, где все контрольные точки находятся на кривой. Я узнал о сплайнах Catmull-Rom из выдающейся серии поваренных книг разработчиков iOS Эрики Садун (очень рекомендую к прочтению).

Если вы создадите сплайн Catmull-Rom с примерно 10 точками, равномерно распределенными по кругу, полученная в результате кривая будет очень близко приближаться к кругу. (Сначала я рекомендовал 8 контрольных точек, но я только что попробовал, и кривая, описывающая полный круг, не выглядит идеально круглой, пока у вас не будет хотя бы 10 контрольных точек.) Если вы хотите сгенерировать CGPath, который содержит дугу с меньший угол, создайте 10 контрольных точек, которые покрывают меньше или больше вашей дуги.

У меня есть проект на Github под названием TrochoidDemo , в котором используются сплайны Catmull Rom для создания реалистично выглядящих волн на воде. Вы можете игнорировать большинство из них, но он включает в себя файл SmoothCGPointsArray.swift, который будет принимать массив контрольных точек и использовать сплайны Catmull-Rom для возврата нового массива с промежуточными точками, которые описывают плавную кривую между вашими контрольными точками.

Функция, которую вы хотите в этом файле: smoothPointsInArray(_:granularity:adjustGranularity:)

Параметр adjustGranularity по умолчанию имеет значение true. Этот параметр указывает функции изменять количество создаваемых точек сглаживания в зависимости от расстояния между контрольными точками. Вы захотите переопределить значение по умолчанию и вызвать функцию с помощью adjustGranularity=false, поскольку для анимации всегда требуется одинаковое количество точек.

Вы бы просто вызвали эту функцию с массивом контрольных точек, который включает 10 точек для каждой дуги, которую вы хотите нарисовать, независимо от угла. Он будет возвращать массив CGPoints, а затем вы будете использовать результирующий массив GCPoints для генерации UIBezierPath с помощью команд lineTo(), а затем подать CGPath из вашего UIBezierPath в анимацию.

В исходном массиве точек, если вы хотите, чтобы заданная точка была острым углом, добавьте эту точку дважды в ряд. Если вы хотите отрезок, добавьте обе конечные точки отрезка дважды.

Вот как выглядит 8-точечная аппроксимация круга: 8-point Catmull-Rom circle

А вот как выглядит 10-точечная аппроксимация круга: 10-point Catmull-Rom circle

(На каждом изображении выше аппроксимация Catmull-Rom изображена черным, контрольные точки показаны синим, а нарисованный Какао круг нарисован синим. Обратите внимание, что Какао использует кривые Безье для создания своих кругов, поэтому Какао круги также являются приблизительными.)

0 голосов
/ 11 января 2019

Вы должны попробовать использовать CADisplayLink

Вот пример:

func setupAnimation() {
    let dispLink = CADisplayLink(target: self, selector: #selector(updateAnimation))
    dispLink.preferredFramesPerSecond = 60 //will update 60 times per second (the default for iOS devices) ... this line of code is optional depending on the fps you want for your animation
    dispLink.add(to: RunLoop.current, forMode: RunLoop.Mode.common)
}

func updateAnimation() {
    //do your animation code here
}

Использование ссылки на дисплей свяжет вашу анимацию с частотой обновления экрана таким образом, чтобы она была плавной, и вы можете в основном делать все, что угодно, не выглядя прерывистым

...