Хороший интерактивный дизайн формирует позитивный пользовательский опыт. Многие компании, которые создают библиотеки компонентов, помимо всего, пишут документацию с разделами по анимации и даже поставляют готовые пакеты помогающие строить переходы и передвижения согласно их спецификации.
В этой статье мы рассмотрим виды анимаций в веб приложениях. Паттерны UX/UI анимаций в дизайн системах и их реализацию на Angular. Также, будет показан способ организации анимаций в библиотеках с учетом переиспользования и кастомизации.

В предыдущих статьях я писал, что мы документируем в наших библиотеках:
типографику
палитру
иконки
сетку
Angular составляющие (компоненты, директивы, пайпы, сервисы)
Недавно мы решили добавить новый раздел - Анимация. Давайте рассмотрим его.
Цели и принципы интерактивного дизайна
В этот раз, добавляется новый пункт - анимация. Если предыдущие пункты очевидны, то последнему даже крупные поставщики библиотек не все уделяют время и внимание. Поэтому, сначала рассмотрим цели и принципы интерактивного дизайна в приложениях:
Дизайн ориентированный на цели - техника, которая помогает пользователю достигать цели в приложении.
Удобство использования - функционал должен быть интуитивно понятным и надежным, тогда пользователь сможет получить удовольствие от работы. Да, это верно и для анимаций.
Отображение функционала - элемент интерфейса должен демонстрировать свое предназначение.
Обучаемость - интерфейсы, которые похожи своим поведением на остальные более предсказуемы и с ними проще разобраться за счет предыдущего опыта пользователя. Поэтому анимации тоже следует документировать и делать переиспользуемыми.
Обратная связь и время отклика - интерфейсы и компоненты должны мгновенно реагировать на действия пользователя. Сюда относится и "видимая производительность". Длительные операции всегда можно скрыть анимацией.
Виды анимаций в браузере
После рассмотрения целей, перейдем к способам создания анимаций. Не будем подробно останавливаться на каждом, но зафиксируем способы их оптимизации. Существует две технологии анимации:
css анимация - позволяет анимировать переходы от одной конфигурации CSS стилей к другой.
Для оптимизации css анимаций нужно знать, что существует свойство will-change, которое подскажет браузеру об изменении элемента. Это повысит отзывчивость интерфейса. Про свойство можно подробней почитать в статье по ссылке.
javascript анимация - создавать более сложные анимации в отличие от css.
Для оптимизации javascript анимации нужно использовать requestAnimationFrame - указывает браузеру на то, что вы хотите произвести анимацию, и просит его запланировать перерисовку на следующем кадре анимации. Читаем подробности по ссылке.
Анимация Angular
Останавливаться на подробном описании создания анимаций в этом разделе не будем, т.к. в интернете полно статей на эту тему, но зафиксируем в виде тезисов ключевые моменты связанные с Angular:
Работают поверх javascript анимаций
Поставляются из коробки с Angular
Удобное API для работы с состоянием компонента. Можно вызывать анимацию на dom элемент, на переменную в шаблоне. Создавать цепочку анимаций.
Есть средства для переиспользования анимаций
Создадим несколько UI/UX паттернов анимации
Паттерн: объяснение происходящего (What Just Happened), добавление элемента. Допустим у нас происходит какое-либо действие в результате которого добавляется новый элемент в список или таблицу. Визуально элемент может быть очень похожим по своим данным и пользователь может не заметить, что новый элемент появился на форме. Это нарушает принципы интерактивного дизайна. Реализуем эту разновидность паттерна с добавлением элемента.
Посмотрим сразу на результат:

Исходный код шаблона:
<div class="table"> <div *ngFor='let item of items; index as i; trackBy:id' @fadeExplainMotion [@.disabled]="isIniting" > .... </div> </div>
Исходный код анимации в компоненте :
animations: [trigger('fadeExplainMotion', [ transition( ':enter', animation([ style({ transform: 'translate(25%,0)', backgroundColor: '#fafafa', height: '30px' }), animate( '300ms cubic-bezier(0.59, 0.32, 0.38, 1.13)', style({ transform: 'translate(0)', height: '82px' }) ), ]) )])]
Опишем, что тут происходит:
@.disabled - отключим анимацию элементов при первой загрузке списка.
@fadeExplainMotion - анимируем элемент при добавлении его в dom.
animations: [...] - блок, который описывает поведение анимации. Тут применяются трансформации к dom элементу, изменение заливки и его высоты. А также задается время и функция движения.
Паттерн: объяснение происходящего (What Just Happened), удаление элемента. Этот вид анимации похож на предыдущий и выглядит следующим образом:

Код анимации:
transition( '* => void', animation([ style({ backgroundColor: '#fafafa', height: '82px' }), animate( 300ms cubic-bezier(0.59, 0.32, 0.38, 1.13), style({ transform: 'translate(-25%,0)', height: '82px' }) ), ])
Код довольно похож за исключением:
* => void - реагируем на удаление элемента из dom
transform: 'translate(-25%,0) - смещает элемент справа на лево
Отлично, мы написали пару анимаций для компонента. Давайте представим, что нам потребовалось использовать их в других приложениях. Читаем дальше, как это сделать.
Сделаем анимацию переиспользуемой и кастомизируемой для пользователей библиотек с использованием dependency injection
После изучения многих библиотек компонентов, можно сделать вывод, что анимации удобно складывать в директории "animation" . Например, так делают ребята из TaigaUI и NgZorro. Этот способ нам тоже показался удобным.
Добавим файл "fade-explain.ts", который экспортирует анимацию выше:
const TRANSITION = '{{duration}}ms cubic-bezier(0.59, 0.32, 0.38, 1.13)'; const DURATION = {params: {duration: 300}}; export const fadeExplainMotion: AnimationTriggerMetadata = trigger('fadeExplainMotion', [ transition( ':enter', animation([ style({ transform: 'translate(25%,0)', backgroundColor: '#fafafa', height: '30px' }), animate( TRANSITION, style({ transform: 'translate(0)', height: '82px' }) ), ]), DURATION ), transition( '* => void', animation([ style({ backgroundColor: '#fafafa', height: '82px' }), animate( TRANSITION, style({ transform: 'translate(-25%,0)', height: '82px' }) ), ]) ) ]);
Теперь метаданные компонента будут выглядеть следующим образом, анимация может подключается в любые компоненты:
@Component({ selector: 'app-list', templateUrl: './list.component.html', styleUrls: ['./list.component.scss'], animations: [fadeExplainMotion, fadeExplainEditMotion, ... ], })
Этот пример демонстрирует переиспользование. Давайте добавим возможность пользователям библиотек компонентов менять параметры анимации, например скорость. Для этого воспользуемся механизмом dependency injection.
создадим файл "animation-tokens.ts"
Напишем реализацию ANIMATIONS_DURATION
export const ANIMATIONS_DURATION = new InjectionToken<number>( 'Duration of animations in ms', { factory: () => 300, }, );
Получим токен в конструкторе компонента:
constructor(@Inject(ANIMATIONS_DURATION) private readonly duration: number,) {
Создадим переменную в компоненте:
animation = { value: '', params: { duration: this.duration } };
Привяжем переменную к анимации в шаблоне:
<div *ngFor='let item of items; index as i; trackBy:id' [@fadeExplainMotion]='animation' >
Для изменения параметра скорости анимации нужно добавить в модуль новое значение токена (в раздел providers):
providers: [ { provide: ANIMATIONS_DURATION, useValue: 350 } ],
Теперь вся команда может использовать единообразные анимации, и при необходимости переопределять свои параметры.
Пример из статьи можно найти по ссылке.
Подборка книг и интересных статей по анимации интерфейсов:
Недавно прошел онлайн workshop "Роман Седов & Александр Инкин | Workshop | DI: две буквы, безграничные возможности." в котором было пару слайдов об анимации и DI.
Interaction Design & Complex Animations - в этой книге можно найти фундаментальные принципы интерактивного дизайна и анимации.
Animation in Design System E-Book - книга рассказывает про анимацию в дизайн системах.
In-Depth guide into animations in Angular - статья подробно описывает анимацию в Angular.
Angular UX Using 4 Animation Techniques - статья с большим набором видео про UX/UI и анимацию на Angular.
Заключение
В этой статье мы определили роль анимаций в дизайн системах и библиотеках компонентов. Подсветили моменты связанные с их оптимизацией. Рассмотрели паттерны анимаций. Реализовали пару примеров на Angular. И применили механизм dependency injection для повторного использования и кастомизации.
