Хороший интерактивный дизайн формирует позитивный пользовательский опыт. Многие компании, которые создают библиотеки компонентов, помимо всего, пишут документацию с разделами по анимации и даже поставляют готовые пакеты помогающие строить переходы и передвижения согласно их спецификации.

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

В предыдущих статьях я писал, что мы документируем в наших библиотеках:

  • типографику

  • палитру

  • иконки

  • сетку

  • Angular составляющие (компоненты, директивы, пайпы, сервисы)

Недавно мы решили добавить новый раздел - Анимация. Давайте рассмотрим его.

Цели и принципы интерактивного дизайна

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

  • Дизайн ориентированный на цели - техника, которая помогает пользователю достигать цели в приложении.

  • Удобство использования - функционал должен быть интуитивно понятным и надежным, тогда пользователь сможет получить удовольствие от работы. Да, это верно и для анимаций.

  • Отображение функционала - элемент интерфейса должен демонстрировать свое предназначение.

  • Обучаемость - интерфейсы, которые похожи своим поведением на остальные более предсказуемы и с ними проще разобраться за счет предыдущего опыта пользователя. Поэтому анимации тоже следует документировать и делать переиспользуемыми.

  • Обратная связь и время отклика - интерфейсы и компоненты должны мгновенно реагировать на действия пользователя. Сюда относится и "видимая производительность". Длительные операции всегда можно скрыть анимацией.

Виды анимаций в браузере

После рассмотрения целей, перейдем к способам создания анимаций. Не будем подробно останавливаться на каждом, но зафиксируем способы их оптимизации. Существует две технологии анимации:

css анимация - позволяет анимировать переходы от одной конфигурации CSS стилей к другой.

Для оптимизации css анимаций нужно знать, что существует свойство will-change, которое подскажет браузеру об изменении элемента. Это повысит отзывчивость интерфейса. Про свойство можно подробней почитать в статье по ссылке.

javascript анимация - создавать более сложные анимации в отличие от css.

Для оптимизации javascript анимации нужно использовать requestAnimationFrame  - указывает браузеру на то, что вы хотите произвести анимацию, и просит его запланировать перерисовку на следующем кадре анимации. Читаем подробности по ссылке.

Анимация Angular

Останавливаться на подробном описании создания анимаций в этом разделе не будем, т.к. в интернете полно статей на эту тему, но зафиксируем в виде тезисов ключевые моменты связанные с Angular:

  • Работают поверх javascript анимаций

  • Поставляются из коробки с Angular

  • Удобное API для работы с состоянием компонента. Можно вызывать анимацию на dom элемент, на переменную в шаблоне. Создавать цепочку анимаций.

  • Есть средства для переиспользования анимаций

Создадим несколько UI/UX паттернов анимации

Паттерн: объяснение происходящего (What Just Happened), добавление элемента. Допустим у нас происходит какое-либо действие в результате которого добавляется новый элемент в список или таблицу. Визуально элемент может быть очень похожим по своим данным и пользователь может не заметить, что новый элемент появился на форме. Это нарушает принципы интерактивного дизайна. Реализуем эту разновидность паттерна с добавлением элемента.

Посмотрим сразу на результат:

Добавление элемента Angular animation
Добавление элемента Angular animation

Исходный код шаблона:

<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), удаление элемента. Этот вид анимации похож на предыдущий и выглядит следующим образом:

Удаление элемента  Angular animation
Удаление элемента Angular animation

Код анимации:

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.

  1. создадим файл "animation-tokens.ts"

  2. Напишем реализацию ANIMATION_OPTIONS

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 для повторного использования и кастомизации.

Комментарии (0)