React.jsのCSSアニメーション

2015-04-18T00:00:00+09:00 JavaScript React.js CSS

公式ドキュメント: https://facebook.github.io/react/docs/animation.html

参考: http://qiita.com/koba04/items/236014c955f8af14d3fc

追加されたり削除されたりした際にCSS Transition等を使ってアニメーションが出来るようにしたいとかまぁあるあるパターンだと思いますが、そういうのもサポートされてる模様

動作の概要のキャプチャ動画

{% youtube DVcBfxiMTOk %}

CSSTransitionGroupを使った場合

var React = require("react/addons"),
    CSSTransitionGroup = React.addons.CSSTransitionGroup;

var Top = React.createClass({
  mixins: [React.addons.LinkedStateMixin],
  getInitialState: function() {
    return { items: [] };
  },
  render: function() {
    var items = this.state.items.map(function(item, i) {
      var key = "ITEM" + (i + 1);

      return (
        <div key={key} className="item">
          {item}
          <button onClick={this.onDelete.bind(this, i)}>x</button>
        </div>
      );
    }.bind(this));

    return (
      <div>
        <input type="text" valueLink={this.linkState("text")} />
        <button onClick={this.onClick}>send</button>
        <div>
          <CSSTransitionGroup transitionName="item">
            {items}
          </CSSTransitionGroup>
        </div>
      </div>
    );
  },
  onClick: function(e) {
    e.preventDefault();

    var items = this.state.items;
    items.push(this.state.text);
    this.setState({ items: items, text: "" });
  },
  onDelete: function(i, e) {
    var items = this.state.items;
    delete items[i];
    this.setState(items);
  }
});

React.render(
  <Top />,
  document.querySelector("#app")
);

っていう感じでCSSTransitionGroupを使えばtransitionNameで指定した値に対応するCSSが作用するようになる。例えば以下の様な感じのCSS作れば良い

.item-enter {
  -webkit-transition: 1s ease-in;
  opacity: 0;
}
.item-enter-active {
  opacity: 1;
}

.item-leave {
  -webkit-transition: 1s ease-in;
  opacity: 1;
}

.item-leave-active {
  opacity: 0;
}

TransitionGroupを使った場合

CSSTransitionGroupとは違いイベントをReactコンポーネント側で管理する方法も取れる

var React = require("react/addons"),
    TransitionGroup = React.addons.TransitionGroup,
    $ = require("jquery");

var Item = React.createClass({
  propTypes: {
    text: React.PropTypes.string
  },
  componentWillEnter: function(callback) {
    $(this.getDOMNode()).css("opacity", "0");
    callback();
  },
  componentDidEnter: function() {
    $(this.getDOMNode()).animate({ opacity: 1 }, { duration: 2000 });
  },
  componentWillLeave: function(callback) {
    $(this.getDOMNode()).animate({ opacity: 0 }, { duration: 2000 }, callback);
  },
  componentDidLeave: function() {
  },
  render: function() {
    var text = this.props.text;

    return (
      <div className="item">
        {text}
         <button onClick={this.props.onDelete}>x</button>
      </div>
    );
  }
});

var Items = React.createClass({
  getInitialState: function() {
    return { items: [] };
  },
  render: function() {
    var items = this.state.items.map(function(data, i) {
      var key = "ITEM" + (i + 1);

      return (
        <Item key={key} text={data} onDelete={this.onDelete.bind(this, i)} />
      );
    }.bind(this));

    return (
      <div>
        <TransitionGroup component="div">
          {items}
        </TransitionGroup>
      </div>
    );
  },
  addItem: function(item) {
    if (item) {
      var items = this.state.items;
      items.push(item);
      this.setState({ items: items });
    }
  },
  onDelete: function(i, e) {
    e.preventDefault();

    var items = this.state.items;
    delete items[i];

    this.setState(items);
  }
});

var Top = React.createClass({
  mixins: [React.addons.LinkedStateMixin],
  getInitialState: function() {
    return { text: "" };
  },
  render: function() {
    return (
      <div>
        <input type="text" valueLink={this.linkState("text")} />
        <button onClick={this.onClick}>send</button>
        <Items ref="items" />
      </div>
    );
  },
  onClick: function(e) {
    e.preventDefault();

    if (this.state.text) {
      this.refs.items.addItem(this.state.text);
      this.setState({ text: "" });
    }
  }
});

React.render(
  <Top />,
  document.querySelector("#app")
);

っていうようにcomponentWillEnter/componentDidEnter/componentWillLeave/componentDidLeaveなイベントメソッドが作用するようになる

まぁざっくりとして使ってみただけなんだけど、詳しくはドキュメントか参考読むと良い。とりあえずハマるのはkeyを持つ場所をミスるとイベントが正しく作用しなかったりする

gradleのExecタスクタイプ react-routerのテストに関して