angular.jsをやってみる (19) - 動的に追加したエレメントに対して$animate.enter -
公式リファレンス: http://docs.angularjs.org/api/ngAnimate/service/$animate
例えばクリックイベントによりノードを追加したりするにあたって、ngAnimateなアニメーションを利用したい場合とかに$animate.enter等を利用する事で出来るっぽいのでやってみた
とりあえず、わかりやすい例としては画面上に通知的なのを出す。それをクリックイベントにてエレメントを追加し$animateを使ってfadeIn/fadeOutなアニメーション的な事をする
index.html
<!DOCTYPE html>
<html ng-app="app">
  <head>
    <script src="jquery.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular-animate.min.js"></script>
    <script src="app.js"></script>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div id="notifications"></div>
    <div ng-controller="Controller1">
      <a href="#" ng-click="click()">click</a>
    </div>
  </body>
</html>
CSSに関しては
.ng-animate {
  -webkit-transition: 3s linear all;
  transition: 3s linear all;
}
#notifications {
  position: absolute;
  top: 10px;
  right: 10px;
}
.notify {
  margin: 10px;
  width: 300px;
  height: 100px;
  line-height: 100px;
  border: 1px solid red;
  border-radius: 3px;
  vertical-align: middle;
  text-align: center;
}
的な所でng-animateクラスな辺りでtransition設定をしておく。これ以前にも書いたので詳細は(ry
app.js
angular.module("app", ["ngAnimate"])
  .animation(".notify", function($timeout, $animate) {
    return {
      enter: function(element, done) {
        element.css("opacity", 0);
        jQuery(element).animate({ opacity: 1 }, function() {
          $timeout(function() {
            $animate.leave(element);
            done();
          }, 5000);
        });
      },
      leave: function(element, done) {
        element.remove();
        done();
      }
    };
  })
  .controller("Controller1", function($scope, $animate) {
    $scope.click = function() {
      var elm = angular.element("<div>");
      elm.addClass("notify");
      elm.text(new Date().getTime());
      // 第2引数で指定しているエレメントに対して第1引数で指定したエレメントが追加されてng-enterなどのCSSクラスが処理される
      $animate.enter(elm, angular.element("#notifications"));
    };
  });
んな感じで動的に追加したエレメントに対して$animate.enterを使う事でCSSクラスに対応したアニメーションを利用できる模様
追記: $animate.enterの第3引数について
  
    angular.jsの$animate.enterの引数は
1. animationに対応するクラスを持ったElement
2. 1を親とするエレメント
3. 1.を追加するにあたって特定のエレメント後に追加する場合にそのエレメント
っていう感じな引数を持つ模様
  
  
  
    — kinjouj (@kinjou_j) 2014, 4月 8
  
  
    http://t.co/tvzhqhh64a でやったやつでは第3引数が指定されていないので第2引数に指定した親となる部分にunshift?される。よってエレメントがアニメーションされるにあたってはエレメントが上に追加されていく
  
  
  
    — kinjouj (@kinjou_j) 2014, 4月 8
  
  
    で第3引数を指定する事により、指定したアニメーションを行うエレメントは第3引数で指定したエレメントの後に追加される事により先のツイートとは違い下に追加されていく
  
  
  
    — kinjouj (@kinjou_j) 2014, 4月 8
  
  
    $animate.enter(elm, angular.element('#notifications'), angular.element('.notify'));
なんてやってもダメ。最初にやる場合においては.notifyなクラスが存在しないからエレメントの挿入がされない
  
  
  
    — kinjouj (@kinjou_j) 2014, 4月 8
  
要は既にあるのならそれよりも下にエレメント追加するようにしたい場合とかだと
angular.module("app", ["ngAnimate"])
  .animation(".notify", function($timeout, $animate) {
    return {
      enter: function(element, done) {
        element.css("opacity", 0);
        jQuery(element).animate({ opacity: 1 }, function() {
          $timeout(function() {
            $animate.leave(element);
            done();
          }, 3000);
        });
      },
      leave: function(element, done) {
        jQuery(element).animate({ opacity: 0 }, done);
      }
    };
  })
  .controller("Controller1", function($scope, $animate) {
    $scope.click = function() {
      var time = new Date().getTime();
      var elm = angular.element("<div>");
      elm.attr("id", time);
      elm.addClass("notify");
      elm.text(time);
      var top = angular.element(".notify").last();
      if (!top.hasClass("notify")) {
        top = null;
      }
      $animate.enter(elm, angular.element("#notifications"), top);
    };
  });
っていう風に既にあるかチェックしてないのならnullで初期化する的な事すりゃいいっぽい