Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Хороший интерактивный дизайн формирует позитивный пользовательский опыт. Многие компании, которые создают библиотеки компонентов, помимо всего, пишут документацию с разделами по анимации и даже поставляют готовые пакеты помогающие строить переходы и передвижения согласно их спецификации.
В этой статье мы рассмотрим виды анимаций в веб приложениях. Паттерны 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"
Напишем реализацию 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 для повторного использования и кастомизации.