Переиспользование анимации
В Angular анимации можно определить стили один раз и использовать их в нескольких компонентах при создании для них анимированных смен состояний (подобно переиспользованию компонентов, директив и др.).
Используя метод animation() опишите анимацию и экспортируйте ее.(untitled)
export const reusableAnimation = animation([ style({ backgroundColor: '{{ backgroundColor }}', fontSize: '{{ fontSize }}', width: '{{ width }}' }), animate('{{ time }}') ]);
В двойных фигурных скобках описаны параметры, передаваемые анимации при ее вызове через useAnimation(). Также допустимо использование константных значений.
Пример такой Angular анимации.(untitled)
@Component({ selector: 'reusable-animation', templateUrl: './reusable-animation.component.html', animations: [ trigger('reusableAnimation', [ transition('initial => expanded', useAnimation(reusableAnimation, { params: { backgroundColor: '#fff', fontSize: '16px', time: '0.3s', width: '100%' } })) ]) ] }) ...
Функция useAnimation() принимает два параметра: первый – анимация, определенная для переиспользования, второй – объект, в свойстве params которого указываются значения параметров.
Сложная анимация
Под сложной анимацией в Angular понимается одновременная или последовательная работа нескольких простых анимаций. В частности, например, она позволяет сделать анимированным появление/исчезание связанной последовательности элементов (пункты списка, строки таблицы).
Реализуется подобное с помощью следующих функций:
- query() – находит один и более дочерних HTML-элементов по заданному критерию в пределах элемента, к которому применяется анимация, и применяет ее к каждому из них;
- stagger() – устанавливает задержку для найденных функцией query() элементов;
- group() – запускает все составляющие анимации параллельно;
- sequence() – запускает все составляющие последовательно.
Рассмотрим применение сложной анимации одновременно к нескольким элементам, используя query() и stagger().(untitled)
animations: [ trigger('appearingItems', [ transition(':enter', [ query('ul.users li', [ style({opacity: 0, transform: 'translateY(-100px)'}), stagger(-50, [ animate('300ms', style({opacity: 1, transform: 'none'})) ]) ]) ]) ]) ]
Здесь Angular анимация appearingItems определяется для появляющихся элементов списка (состояние :enter) с классом стилей .users.
Первым параметром query() передается селектор (критерий поиска элементов), а вторым – массив с описанием анимации, где первое использование функции style() задает исходные стили для элементов, попадающих под критерий поиска.
Теперь перейдем к примеру с использованием group().(untitled)
animations: [ trigger('groupAnimation', [ transition(':enter', [ style({transform: 'translateX(-100px)', opacity: 0}), group([ animate('0.3s ease', style({transform: 'translateX(0)'})), animate('0.2s 0.15 ease', style({opacity: 1})) ]) ]) ]) ]
Сразу стоит отметить, что функция group() группирует не элементы, а стадии составной анимации применительно к одному элементу, которые работают одновременно и независимо друг от друга.
Как и в предыдущем примере, использование style() задает исходные стили элемента. Далее с помощью group() для каждого свойства задается своя конфигурация анимирования.
Для выполнения этой же Angular анимации последовательно без использования задержки, используется функция sequence().(untitled)
animations: [ trigger('sequenceAnimation', [ transition(':enter', [ style({transform: 'translateX(-100px)', opacity: 0}), sequence([ animate('0.3s ease', style({transform: 'translateX(0)'})), animate('0.2s 0.15 ease', style({opacity: 1})) ]) ]) ]) ]
Анимированная смена маршрутов
Анимация маршрутов требует понимания работы модуля маршрутизации Angular.
Фактически переход с одного URL приложения на другой – это просто смена представлений (change views). Используя анимацию Angular можно сделать смену маршрутов анимированной.app-routing.module.ts
const routes: Routes = [ { {path: 'page1', component: Page1Component, data: {animation: 'page1'}}, {path: 'page2', component: Page2Component, data: {animation: 'page2'}} } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
app.component.html
<div [@routeChangeAnimation]="getRouteAnimationState(outlet)" > <router-outlet #outlet="outlet"></router-outlet> </div>
app.component.ts
@Component({ selector: 'app-root', templateUrl: 'app.component.html', animations: [ routeChangeAnimation ] }) export class AppComponent{ constructor(){} getRouteAnimationState(outlet: RouterOutlet) { return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation']; } }
change-route-animation.ts
export const routeChangeAnimation = trigger('routeChangeAnimation', [ transition('page1 <=> page2', [ style({position: 'relative'}), query(':enter, :leave', [ style({ position: 'absolute', top: 0, left: 0, width: '100%' }) ]), query(':enter', [ style({ left: '-100%'}) ]), query(':leave', animateChild()), group([ query(':leave', [ animate('300ms ease-out', style({left: '100%'})) ]), query(':enter', [ animate('300ms ease-out', style({left: '0%'})) ]) ]), query(':enter', animateChild()), ]) ]);
Теперь по порядку. Для определения анимированной смены представления при смене URL используется свойство animation (название может быть другим), указанное в свойстве маршрута data. В качестве значения свойству animation задается имя состояния анимации.
В шаблоне компонента (app.component.html), в котором будет происходить загрузка представления по запрашиваемому URL, элемент <router-outlet> является дочерним по отношению к элементу <div>, для которого определяется routeChangeAnimation. Для определения имени состояния используется метод getRouteAnimationState(), извлекающий значение свойства animation, заданное для текущего маршрута.
Определение самой Angular анимации ограничивается в данном случае описанием смены между собой пары состояний page1 и page2.
Поскольку в момент смены состояний новое представление вставляется сразу же после предыдущего, элементу, для которого определен триггер, задается относительное позиционирование, а дочерним по отношению к нему элементам – абсолютное. Это нужно для избежания одновременного появления на странице двух представлений.(untitled)
style({position: 'relative'}), query(':enter, :leave', [ style({ position: 'absolute', top: 0, left: 0, width: '100%' }) ])
Здесь query() используется для задания исходных стилей выборке элементов.
Далее представление маршрута, на который осуществляется переход, скрывается сдвигом влево.(untitled)
query(':enter', [ style({ left: '-100%'}) ]),
А в представлении, с которого происходит переход, инициируется с помощью функции animateChild() вызов его дочерних анимаций.(untitled)
query(':leave', animateChild()),
Для понимания, в коде ниже анимация childAnimation является дочерней по отношению к анимации parentAnimation:(untitled)
<div [@parentAnimation]="getState()"> <div [@childAnimation]="getChildState()"></div> </div>
Таким образом, если функция childAnimate() вызывается в parentAnimation, то будет запущена анимация childAnimation.
После функция group() запускает одновременно анимированную смену представлений. Старое представление сдвигается за пределы окна вправо, а новое, которое было изначально спрятано слева, появляется.(untitled)
group([ query(':leave', [ animate('300ms ease-out', style({left: '100%'})) ]), query(':enter', [ animate('300ms ease-out', style({left: '0%'})) ]) ])
И в конце инициируется запуск дочерних Angular анимаций нового представления.(untitled)
query(':enter', animateChild())
Свежие комментарии