概要
- ページ遷移する時にスライドするようなアニメーションを入れてみます
- Angularにアニメーションの機能も搭載されているのでそれを使います
サンプル
ベースのアプリ作成
- まずは適当な3つのページを用意します
% tree src/app
src/app
├── animations.ts
├── app-routing.module.ts
├── app.component.ts
├── app.module.ts
├── page1
│ └── page1.component.ts
├── page2
│ └── page2.component.ts
└── page3
└── page3.component.ts
- 各ページは背景色が違うだけでほとんど同じ内容です
src/app/page1/page1.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-page1',
template: `
<style>
main {
height: 100vh;
width: 100vw;
background-color: lightblue;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
</style>
<main>
<p>page1 works!</p>
<p>
<span>Prev</span> | <a routerLink="/page2">Next</a>
</p>
</main>
`,
})
export class Page1Component {}
- styleが多いですが真ん中中央寄せにしているだけです
アニメーションを追加
-
app.module.ts
にアニメーションを使うためのBrowserAnimationsModule
を追加します
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
// importを追加
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
import { Page3Component } from './page3/page3.component';
@NgModule({
declarations: [AppComponent, Page1Component, Page2Component, Page3Component],
// BrowserAnimationsModuleを追加
imports: [BrowserModule, BrowserAnimationsModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
- routingの設定に識別子を追加します
- これはこの後で、どのページからどのページに遷移した時にどのアニメーションを適用する、といった感じの定義をする時に使います
src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
import { Page3Component } from './page3/page3.component';
const routes: Routes = [
{ path: '', redirectTo: 'page1', pathMatch: 'full' },
// それぞれdataプロパティを追加
{ path: 'page1', component: Page1Component, data: { animation: 'Page1' } },
{ path: 'page2', component: Page2Component, data: { animation: 'Page2' } },
{ path: 'page3', component: Page3Component, data: { animation: 'Page3' } },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
-
<router-outlet>
を定義している部分に処理を追加します
src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
// <router-outlet>をdivで囲ってアニメーションの設定を追加
template: `
<div [@routeAnimations]="prepareRoute(outlet)">
<router-outlet #outlet="outlet"></router-outlet>
</div>
`,
})
export class AppComponent {
title = 'ng-animation-sample';
prepareRoute(outlet: RouterOutlet) {
return (
outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation
);
}
}
-
routeAnimations
はこの後定義します -
prepareRoute
実行時に<router-outlet>
の情報を渡すことで今表示しているページの情報を取得できます-
outlet.activatedRouteData.animation
でapp-routing.module.ts
で設定したanimation: 'Page1'
といったデータを取り出してreturnしています
-
- 最後に
animation.ts
にアニメーションの情報を定義します
src/app/animation.ts
import {
trigger,
animate,
transition,
query,
style,
group,
animateChild,
} from '@angular/animations';
export const slideInAnimation = trigger('routeAnimations', [
transition('Page1 => Page2, Page2 => Page3', [
style({ position: 'relative' }),
query(':enter, :leave', [
style({
position: 'absolute',
top: 0,
right: 0,
width: '100%',
}),
]),
query(':enter', [style({ right: '-100%' })]),
query(':leave', animateChild()),
group([
query(':leave', [animate('300ms ease-out', style({ right: '100%' }))]),
query(':enter', [animate('300ms ease-out', style({ right: '0%' }))]),
]),
query(':enter', animateChild()),
]),
transition('Page3 => Page2, Page2 => Page1', [
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()),
]),
]);
- この内容は公式サイトの内容をベースにしています
-
transition
の定義が2つあります-
transition('Page1 => Page2, Page2 => Page3'
はPage1からPage2への遷移かPage2からPage3への遷移の際に発動します -
transition('Page3 => Page2, Page2 => Page1'
はPage3からPage2への遷移かPage2からPage1への遷移の際に発動します - これは先程の
[@routeAnimations]="prepareRoute(outlet)"
の部分でページの情報を随時拾っているので判断できるというわけです
-
- 最後にこの定義を反映させます
src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
// importを追加
import { slideInAnimation } from './animations';
@Component({
selector: 'app-root',
template: `
<div [@routeAnimations]="prepareRoute(outlet)">
<router-outlet #outlet="outlet"></router-outlet>
</div>
`,
// アニメーションの設定を追加
animations: [slideInAnimation],
})
export class AppComponent {
title = 'ng-animation-sample';
prepareRoute(outlet: RouterOutlet) {
return (
outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation
);
}
}
-
animations: [slideInAnimation]
の部分でアニメーションを適用しています - これで完成しているはずなので動かしてみます
- うまくいきました!
感想
- Angular以外のライブラリを使っている場合アニメーションをやろうとするとライブラリ選定から悩むことになりますが、3rdパーティライブラリを使わずできちゃうところがいいですね
- 公式サイトのドキュメントも親切でした