Angular 2でCSSアニメーションを書く


どうも、らこです。 [Angular 2の勉強会で出た質問](http://qiita.com/laco0416/items/ff08a496ecd53403bffc#%E5%87%BA%E3%81%9F%E8%B3%AA%E5%95%8F)でアニメーションのことがあったので、試してみたら問題なく動いたので使い方を紹介します。

## はじめに

Angular 2のメインパッケージに含まれているのは`angular2/animate`モジュールです。これはもう実用段階のAPIです。
それとは別に、`ngAnimate`というプロジェクトも動いており、これはアノテーションベースでアニメーションを宣言的に記述できるようにしようというものです。まだ全然出来てないのでいまは使えませんがデザインドキュメントはみることができます。

[Animations for Angular 2 Beta - Google ドキュメント](https://docs.google.com/document/d/1rTJKNJ6Rv2N5jElLIrrukuEsS_kFOQPH1D5O1Dfva44/edit#heading=h.tboosatyi2r7)

今回紹介するのは前者の今使える方です。

## 完成形
マウスオーバーで背景を指定された色に変更するアニメーションが動くディレクティブを書きました。

いろいろごちゃ混ぜですが動くサンプルは[ここにあります](https://angular2-example.firebaseapp.com)


```ts
import {Directive, Input, HostListener, AfterViewInit} from "angular2/core";
import {AnimationBuilder} from "angular2/animate";

@Directive({
  selector: "[egAnimate]"
})
export class AnimateDirective implements AfterViewInit {
  @Input() color: string;
  private originalColor: string;

  constructor(private animate: AnimationBuilder) {
  }

  ngAfterViewInit(): any {
    this.color = this.color || "red";
  }

  @HostListener("mouseover", ["$event.target"])
  onMouseOver(el: HTMLElement) {
    this.originalColor = el.style.color;
    return this.animate.css()
      .setFromStyles({
        "background-color": this.originalColor,
      })
      .setToStyles({
        "background-color": this.color,
      })
      .setDuration(1000)
      .start(el);
  }

  @HostListener("mouseleave", ["$event.target"])
  onMouseLeave(el: HTMLElement) {
    return this.animate.css()
      .setFromStyles({
        "background-color": this.color
      })
      .setToStyles({
        "background-color": this.originalColor
      })
      .setDuration(1000)
      .start(el);
  }
}
```

本稿では`@Input`や`@HostListener`、`AfterViewInit`については省略します。

## 準備

`angular2/animate`はAngular 2本体に同梱されているので、単純にimportするだけでいいです。

```ts
import {AnimationBuilder} from "angular2/animate";
```

`AnimationBuilder`は特にproviderを書かなくてもComponentかDirectiveのコンストラクタなら最初からDIできるようになっています。

```ts
	constructor(private animate: AnimationBuilder) {
	}
```

## AnimationBuilderの使い方
`AnimationBuilder`は、アニメーションを手続き的に組み立てるためのAPIです。
アニメーションを組み立てるには、まず初めに`css()`関数を呼び出し、CSSアニメーションの組み立てを始めます。
(おそらく将来的にはCSS以外にもなんかサポートするのかもしれません)

```ts
this.animate.css()
```

`css()`関数の戻り値は`CssAnimationBuilder`です。`CssAnimationBuilder`は以下のような機能でアニメーションを組み立てられます。

* クラスを追加する
* クラスを除去する
* スタイルをセットする
* アニメーションの時間を設定する
* アニメーションが始まるまでの遅延時間を設定する
* アニメーションが終わった時にコールバックを受け取る

### クラスを追加する・削除する
動的にクラスを追加するには`addClass`関数、または`addAnimationClass`関数を使います。
`addClass`関数はアニメーションの開始時にクラスを追加するだけです。`addAnimationClass`関数はそれに加えてアニメーションが終了した時に自動でそのクラスを除去します。

```ts
animate.css()
	.addClass("some-class")
	.addAnimationClass("some-temporary-class")
```

### スタイルをセットする
動的にCSSのスタイルを変更するには`setStyles`、`setFromStyles`、`setToStyles`の3つの関数を使います。

`setStyles`はアニメーション開始時にセットする初期のスタイルと、終了時にセットするスタイルを一度に設定できます。

```ts
animate.css()
	.setStyles({
		"background-color": "white",
	},{
		"background-color": "red",
	})
```

`setFromStyles`と`setToStyles`は初期スタイルと終了スタイルを個別に設定できるAPIです。

```ts
animate.css()
	.setFromStyles({
		"background-color": "white",
	})
	.setToStyles({
		"background-color": "red",
 	})
```

### アニメーションの時間を設定する
`setDuration`関数でアニメーションの開始から終了までの時間を設定します。具体的にはCSSの`transition-duration`が設定されます。

```ts
animate.css()
	.setDuration(1000)
```

### アニメーションの開始までの遅延時間を設定する
`setDelay`関数でアニメーションの開始までの時間を設定します。具体的にはCSSの`transition-delay`が設定されます。

```ts
animate.css()
	.setDelay(1000)
```

### アニメーションを開始する
`CssAnimationBuilder`でアニメーションを組み立て終わったら、`start()`関数でアニメーションを開始します。`start()`関数には`HTMLElement`型のオブジェクトを渡します。

```ts
this.animate.css()
	.setFromStyles({
		"background-color": this.originalColor,
  	})
  	.setToStyles({
		"background-color": this.color,
  	})
  	.setDuration(1000)
  	.start(el)
```

`start()`関数が実行された瞬間からアニメーションが開始します。

### アニメーションの終了でコールバックを受け取る
`start()`関数の戻り値は`Animation`型ですが、`Animation`は内部でPromiseを持っているので、終了時に処理を実行できます。`onComplete()`関数にコールバック関数を渡せばアニメーション終了時に一度だけ呼びだされます

```ts
this.animate.css()
	.setFromStyles({
   		"background-color": this.originalColor,
  	})	
  	.setToStyles({
    	"background-color": this.color,
  	})
  	.setDuration(1000)
  	.start(el)
  	.onComplete(()=> {
    	console.log("animated!")
  	});
```

## まとめ
`angular2/animate`めっちゃ良いので使いましょう。`setTimeout`と`Renderer`で頑張るのやめよう！