AngularJS1でngRouteを使ったページ遷移時に、それまでの画面が奥に引っ込むアニメーションや、逆に出てくるアニメーションを入れて、見た目をリッチにしてみた。

デモ https://gyazo.com/1482322c5e4de7c3f95fb243608ac7c8

AngularJSを使ったHTMLページ作成

デモ1

JSでAngularJSのモジュールappとコントローラAppControllerを定義する:

// script.js
(function() {
  'use strict'

  angular.module('app', [])
    .controller('AppController', function() {
      // 特になにもなし
    })
})()
  • 今のところ定義しただけで、特に何もしない

HTMLで、それらを使ったHTMLを定義する:

<!--index.html-->
<!DOCTYPE html>
<html ng-app="app">
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="style.css">

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-rc.1/angular.min.js"></script>
    <script src="script.js" type="text/javascript"></script>
  </head>

  <body ng-controller="AppController as app">
    Hello
  </body>
</html>

スタイルは適当に:

/* style.css */
html, body {
  height: 100%;
}
  • 画面いっぱいに表示されるように、htmlbodyheight: 100%を指定

ngRouteによるページ遷移

ngRouteというモジュールを使うと、同一ページ内で擬似的なページ遷移を行えるようになる。

デモ2

JSでルーティングの設定をする:

// script.js
  angular.module('app', ['ngRoute'])
    ...
    .config(['$routeProvider', function($routeProvider) {
      $routeProvider
        .when('/', {
          templateUrl: 'top',
        })
        .when('/setting', {
          templateUrl: 'setting',
        })
    }])
  • 依存するモジュールにngRouteを指定する
  • $routeProviderにルーティングを定義する
  • templateUrlにパスを指定するが、後述のng-templateで定義した名前を指定するとその中身が参照されることになる
<!-- index.html -->
    <!-- head内 -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-rc.1/angular-route.min.js"></script>

  <body ng-controller="AppController as app">
    <div ng-view id="root"></div>

    <script id="top" type="text/ng-template">
      <div class="top">
        <h3>Top page</h3>
        <a href="#/setting">Go to setting</a>
      </div>
    </script>

    <script id="setting" type="text/ng-template">
      <div class="setting">
        <h3>Setting</h3>
        <a href="#/">Back</a>
      </div>
    </script>
  </body>
  • ng-viewで指定した要素のところに、ルーティングで設定された内容が埋め込まれる
  • type="text/ng-template"を指定したスクリプトタグで、ルーティングで表示される内容を定義できる
  • リンクのURLは<a href="#/〜">でハッシュとして指すことができる
/* style.css */
#root {
  width: 100%;
  height: 100%;
  position: absolute;
}

.top {
  background-color: red;
  width: 100%;
  height: 100%;
}

.setting {
  background-color: green;
  width: 100%;
  height: 100%;
}
  • ng-view で指定した要素を position: absolute にする(後でアニメーションさせるときに必要)

アニメーションの導入

デモ3

<!-- index.html -->
    <!-- head内 -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-rc.1/angular-animate.min.js"></script>

  <body ng-controller="AppController as app"
        class="scaledown-frombottom">
    ...
  • rootじゃなくてそれより上の要素(ここではbody)にクラスアトリビュートをセットして、アニメーションを選択する
// script.js
  angular.module('app', ['ngRoute', 'ngAnimate'])
  ...
  • 依存するモジュールにngAnimateを追加
/* style.css */
body {
  background-color: black;
}

#root {
  ...
  transition: 500ms;
}

.scaledown-frombottom .ng-enter {
  top: 100%;
}

.scaledown-frombottom .ng-enter.ng-enter-active {
  top: 0%;
}

.scaledown-frombottom .ng-leave {
  transform: scale(1);
  opacity: 1;
}

.scaledown-frombottom .ng-leave.ng-leave-active {
  transform: scale(0.75);
  opacity: 0.5;
  z-index: -1000;
}
  • bodybackground-color: black で、背景を黒くする
  • #roottransition: 500ms で、アニメーションの時間を指定する
  • ここではscaledown-frombottomというクラス名のアニメーションを定義している
    • scaledown-frombottomの直後のスペースは必要で、これで階層のついた要素のスタイルを指定できる?
  • 奥に引っ込む時に、z-indexを負の値にしてやり、奥に描画されるようにする
    • transform: translate3d()を使ったら描画の優先順序をいい具合にしてくれないかと思ったが、そうはいかず…

アニメーションの種類を設定

ページを戻る時には別のアニメーションをすることで、「行った」「戻った」というページ間の関係を分かりやすくする。

デモ4

// script.js
  // appモジュール
    .controller('AppController', ['$rootScope', function($rootScope) {
      $rootScope.pageTransitionAnimType = ''
    }])
    ... // ルーティングの設定
    .directive('pageTransitAnim', ['$rootScope', function($rootScope) {
      return {
        restrict: 'A',
        link: function(_scope, elem, attrs, _ctrl) {
          angular.element(elem).on('click touchstart', function() {
            $rootScope.pageTransitionAnimType = attrs.pageTransitAnim
          })
        },
      }
    }])
  • pageTransitAnim というディレクティブを定義して、アニメーションを指定できるようにする
    • リンクがクリックされた時に、そのアトリビュートの値を$rootScope.pageTransitionAnimTypeに代入してやる
<!-- index.html -->
  <body ng-controller="AppController as app"
        ng-class="pageTransitionAnimType">
    ...

    <!-- topからsettingへのリンク -->
        <a href="#/setting" page-transit-anim="scaledown-frombottom">Go to setting</a>

    <!-- settingからtopへのリンク -->
        <a href="#/" page-transit-anim="movetobottom-scaleup">Back</a>
  • bodyng-class="pageTransitionAnimType"で、AngularJSの$rootScope.pageTransitionAnimTypeがクラス名として設定され、JS内で値を変更することで再生するアニメーションが切り替わる
  • 定義されたディレクティブpage-transit-animaタグのアトリビュートとして、そのリンクがクリックされた時にアニメーションを指定する
/* style.css */
.movetobottom-scaleup .ng-enter {
  transform: scale(0.75);
  opacity: 0.5;
  z-index: -1000;
}

.movetobottom-scaleup .ng-enter.ng-enter-active {
  transform: scale(1);
  opacity: 1;
  z-index: 0;
}

.movetobottom-scaleup .ng-leave {
  top: 0%;
}

.movetobottom-scaleup .ng-leave.ng-leave-active {
  top: 100%;
}
  • 追加のアニメーションmovetobottom-scaleupを定義する

参考