Когда я увидел демо ниже, я подумал: Отлично, как они это сделали? Это может показаться удивительным, если вы знаете, что я написал для этого API. В этой статье я опишу, как три ортогональные функции, общая навигация, триггеры событий и треки ключевых кадров объединяются для создания этой приятной анимации.

https://videopress.com/v/ziEUG5qo

Пример предохранителя

Анимация по ключевым кадрам

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

Давайте посмотрим на половину анимации, движение слева от центра.

Наш анимационный движок построен на перемещении объектов с того места, где они обычно находятся. В этом случае каждая карточка имеет положение на экране, определяемое механизмом компоновки. Этот Move говорит, как отодвинуть его от этой позиции. RelativeTo="Size" также использует размер макета: дробные значения для X и Y кратны размеру элемента. Вы можете визуализировать путь в голове или просто еще раз посмотреть на анимацию.

Сплайновая дорожка

Зубчатый путь в этом примере хорошо работает, но не всегда желателен. Вместо этого мы могли бы поставить <Move KeyframeInterpolation="Smooth">, чтобы создать путь с закругленными углами — он создает кривую сплайна из значений.

Аниматоры используют динамическую композицию для реализации пути. Если используется Keyframe, используется поставщик SplineTrack, в противном случае используется поставщик EasingTrack. Есть еще пара для работы с дискретными значениями.

Как он создает плавную кривую? Короче говоря, я использую уравнение Коханека-Бартельса для получения касательных, которые превращаются в кубический сплайн Эрмита. В итоге я использовал точно такой же подход (даже тот же код), чтобы создать плавную кривую на нашем векторном рисунке. Подробнее об этом можно прочитать в моей сплайновой статье.

Анимация не знает навигации

Move указывает полный путь, но карты перемещаются только на часть расстояния при каждом переходе. Вот тут-то и появляется линейная навигация.

Сложная часть Fuse заключалась в том, чтобы определить систему навигации, в которой не было предварительно запеченных анимаций. Мы хотели дать дизайнерам полную свободу выбора того, как навигация будет выглядеть на экране. Чтобы получить это «правильно», потребовалось много итераций в коде. Эта история все еще проявляется в интерфейсах для навигации — у меня есть нерешенная проблема, чтобы немного это исправить. Хорошо, но как это работает?

Каждая карточка представляет собой страницу в LinearNavigation, что дает им определенный порядок в навигации (вы можете увидеть порядок влево-вправо в демонстрации). Активной странице присваивается прогресс 0 для навигации. Страница «перед» ней получает прогресс 1, страница сразу «позади» -1. Все страницы получают прогресс в зависимости от их расстояния до активной страницы. Вторая страница после активной имеет -2, третья -3 и т.д.

Эти значения непрерывны. Когда навигация переключает страницы, она постепенно изменяет значения; например, от 0 до 1 для страницы, перемещающейся вперед. В этом конкретном примере навигация настроена на использование замедления «CircularOut» для перехода.

Ключевым моментом здесь является то, что навигация имеет дело только с этими значениями прогресса. Он в блаженном неведении о визуальном представлении страниц. Как же тогда достигается анимация?

Масштабные аниматоры

Показанный выше Move установлен внутри триггера EnteringAnimation.

EnteringAnimation подписывается на события обновления хода выполнения на странице (при этом используется C#/Uno event). Он преобразует значение прогресса страницы в значение прогресса для временной шкалы Move — он ищет это новое значение. По умолчанию он отображает прогресс от 0 до 0% и прогресс от 1 до 100%.

Элементы Keyframe определяют параметр Time, который указывается в секундах. Когда управляется чем-то вроде EnteringAnimation, фактическая часть секунд игнорируется — временная шкала нормализуется от 0% до 100% прогресса и управляется значением прогресса страницы.

Мы не хотим анимировать всю временную шкалу Move. Мы лишь хотим пройти немного пути. Это то, что делает Scale="0.25". Значение прогресса страницы умножается на эту сумму. Таким образом, страница с прогрессом 1 будет искать только 25% анимации. Страница с прогрессом 2, находящаяся в двух шагах от активной, будет стремиться к 50% на временной шкале. Эти масштабированные значения соответствуют значениям Time, указанным в файле Keyframe.

Аналогом EnteringAnimation является ExitingAnimation. Мы видели, что значения прогресса страницы могут быть положительными или отрицательными. EnteringAnimation имеет дело только со значениями позиций. ExitingAnimation имеет дело с отрицательными значениями — преобразование -1 в 100%. Вот как мы можем обеспечить различные анимации для страниц «перед» и «за» активной страницей.

Имена «Вход» и «Выход» были выбраны на основе первых случаев использования, когда страницы визуально появлялись на экране и покидали его. На самом деле это было сделано до появления механизма продвижения страницы. Мы часто спорили об этих названиях, но так и не смогли придумать что-то более ясное. Никому не понравилось мое PositivePageProgressAnimation предложение.

Другие триггеры

Эта демонстрация — хороший пример ценности ортогонального API:

  • Система навигации заботится только об изменении значения прогресса страницы. Будь то синхронизированный переход или пользователь, проводящий пальцем по экрану, все это сопоставляется с этим простым значением прогресса.
  • Система триггеров, такая как EnteringAnimation, заботится только о преобразовании этого значения в прогресс анимации. Это упрощает создание других триггеров, таких как ExitingAnimation или ActivatingAnimation и DeactivatingAnimation, если направление не имеет значения. Это также позволяет использовать WhileActive или WhileInactive, которые могут указывать Treshhold для того, насколько активными они должны быть.
  • Временная шкала Move — это обычный аниматор, и ему все равно, что им движет. Сюда так же легко можно было бы подключить Rotate или Scale. Keyframe в равной степени относится к любому из этих аниматоров.

Приятно видеть, что это работает так хорошо в этой демонстрации.

Первоначально опубликовано на сайте mortoray.com 4 мая 2017 г.