LoginSignup
8
10

More than 3 years have passed since last update.

[Angular]ページ遷移にアニメーションをつける

Last updated at Posted at 2020-02-05

概要

  • ページ遷移する時にスライドするようなアニメーションを入れてみます
  • 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が多いですが真ん中中央寄せにしているだけです

before.gif

アニメーションを追加

  • 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.animationapp-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]の部分でアニメーションを適用しています
  • これで完成しているはずなので動かしてみます

after.gif

  • うまくいきました!

感想

  • Angular以外のライブラリを使っている場合アニメーションをやろうとするとライブラリ選定から悩むことになりますが、3rdパーティライブラリを使わずできちゃうところがいいですね
  • 公式サイトのドキュメントも親切でした
8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10