Reactで簡単なアニメーションを実装する

ReactのCSSTransitionGroupを利用して簡単なエフェクトを実装します。CSSTransitionGroupはCSSのアニメーションをReactで手軽に利用できるように作られたものです。

Animation | React

CSSTransitionGroupの使い方

CSSTransitionGroupコンポーネントで子の要素を囲む事で、その子の要素の追加や削除のタイミングでアニメーション用のCSSクラスを自動的に付与してくれます。

たとえば、下記のようにitemsを囲ったとすると、

<CSSTransitionGroup transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={300}>
  {items}
</CSSTransitionGroup>

transitionNameで指定した名称をベースに、CSSTransitionGroupの子の要素であるitemsにexample-enter, .example-enter-active, .example-leave, .example-leave-activeを自動的に付与したり削除してくれます。この動きを利用して、事前に下記のようにCSSを定義しておけばCSSのtransitionでアニメーションを実現できるというわけです。

.example-enter {
  opacity: 0.01;
}

.example-enter.example-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

.example-leave {
  opacity: 1;
}

.example-leave.example-leave-active {
  opacity: 0.01;
  transition: opacity 300ms ease-in;
}

マニュアルにあったTodoListのサンプルをJSFiddleで書いてみました。

ポイントは下記の各子要素にkeyを振っているところで、CSSTransitionGroupはこのkeyの値で追加されたものか、あるいは削除されるものかを判断しCSSクラスの付与および削除を行うようです。

    var items = this.state.items.map(function(item, i) {
      return (
        <div key={item} onClick={this.handleRemove.bind(this, i)}>
          {item}
        </div>
      );
    }.bind(this));

フェードイン・フェードアウトはどう実装する?

紙芝居みたいなものを実装したかったので、フェードイン・フェードアウトに挑戦してみました。

CSSTransitionGroupの子の要素は1つでそれを置換する実装をしています。ただ、動かしてみると分かりますが期待通りの動作になっていません。置換された要素が置換後に.example-leave.example-leave-activeのtransitionが完了するまで残っているので、transition中は2つ要素が表示されてしまっています。

要素が重なってくれれば良いわけなので、CSSのpositionにabsoluteを指定して位置を固定しました。

これで期待通りの動きになりました。CSSTransitionGroup自体がやってくれる事はとてもシンプルですね。このCSSTransitionGroupの動きの特性に合わせてCSSを中心にどのように動きを作るか考えるという感じでしょうか。

初回マウント時のアニメーション

実は上記の実装だけだと、CSSTransitionGroupを持つReactコンポーネントが初めてマウントされたタイミングでは子要素へのCSSクラスの付与などが行われません。初回マウント時は異なるtransitionAppearというプロパティをtrueで渡す必要があります。

<ReactCSSTransitionGroup transitionName="example" transitionAppear={true} transitionAppearTimeout={500}>
  <h1>Fading at Initial Mount</h1>
</ReactCSSTransitionGroup>

すると、example-appear, example-appear-activeというCSSクラスが付与されます。先ほどと同様にCSSで下記のように定義しておけばアニメーションしてくれます。

.example-appear {
  opacity: 0.01;
}

.example-appear.example-appear-active {
  opacity: 1;
  transition: opacity .5s ease-in;
}

さきほどのサンプルにもtransitionAppearを追加しました。下記のiFrameだと初回マウント時が分かりにくいので、動きが気になる場合はJSFiddleのサイトから実行して動作を確認してみて下さい。