애니메이션 Edit on GitHub

React에는 애니메이션을 위한 저 수준 API로 ReactTransitionGroup 애드온 컴포넌트가 있고 간단히 기초 CSS 애니메이션과 트랜지션을 구현할 수 있는 ReactCSSTransitionGroup가 있습니다.

고 레벨 API: ReactCSSTransitionGroup #

ReactCSSTransitionGroupReactTransitionGroup를 기반으로 React 컴포넌트가 DOM에 들어가거나 나올때의 CSS의 트랜지션과 애니메이션을 구현하기 쉽게합니다. 이는 ng-animate 라이브러리에 영향을 받았습니다.

시작하기 #

ReactCSSTransitionGroupReactTransitions을 위한 인터페이스입니다. 이는 애니메이션을 제어할 모든 컴포넌트를 감싸는 하나의 엘리먼트 입니다. 아래는 목록의 아이템을 페이드 인/아웃하는 간단한 예제입니다.

var ReactCSSTransitionGroup = require('react-addons-css-transition-group');

var TodoList = React.createClass({
  getInitialState: function() {
    return {items: ['hello', 'world', 'click', 'me']};
  },
  handleAdd: function() {
    var newItems =
      this.state.items.concat([prompt('Enter some text')]);
    this.setState({items: newItems});
  },
  handleRemove: function(i) {
    var newItems = this.state.items;
    newItems.splice(i, 1);
    this.setState({items: newItems});
  },
  render: function() {
    var items = this.state.items.map(function(item, i) {
      return (
        <div key={item} onClick={this.handleRemove.bind(this, i)}>
          {item}
        </div>
      );
    }.bind(this));
    return (
      <div>
        <button onClick={this.handleAdd}>Add Item</button>
        <ReactCSSTransitionGroup transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={300}>
          {items}
        </ReactCSSTransitionGroup>
      </div>
    );
  }
});

주의:

ReactCSSTransitionGroup의 모든 자식은 key 어트리뷰트를 반드시 만들어야 합니다. 한 아이템을 렌더할 때도 예외는 아닙니다. 키는 React가 어떤 자식이 들어오고, 나가고, 머무르는지 파악할 때 사용합니다.

이 컴포넌트에서 새로운 아이템이 ReactCSSTransitionGroup에 추가되면 example-enter 아이템은 CSS 클래스를 가지게 되고 다음 순간에 example-enter-active CSS 클래스가 추가됩니다. 이는 transitionName prop을 기반으로 한 관례입니다.

이 클래스들은 CSS 애니메이션이나 트랜지션을 일으키는데 사용할 수 있습니다. 예를 들어, 이 CSS를 넣은 후 아이템을 추가해 보세요.

.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;
}

에니메이션 기간이 CSS와 렌더 메소드 양쪽에 지정될 필요가 있다는 것에 주의하셔야 합니다. 이는 엘리먼트에서 애니메이션 클래스를 제거할 때 (만약 남아있다면) DOM에서 엘리먼트를 제거할 때 React에 알려줍니다.

처음 마운트에서 애니메이션 하기 #

ReactCSSTransitionGroup은 컴포넌트를 처음 마운트할 때 추가 트렌지션 단계를 추가하기 위해, 선택적인 prop transitionAppear를 제공합니다. 일반적으로 처음 마운트할 때 트렌지션 단계를 넣지 않기 때문에 transitionAppear의 기본 값은 false입니다. 뒤의 예제는 transitionAppear prop에 true 값을 넘기고 있습니다.

  render: function() {
    return (
      <ReactCSSTransitionGroup transitionName="example" transitionAppear={true} transitionAppearTimeout={500}>
        <h1>Fading at Initial Mount</h1>
      </ReactCSSTransitionGroup>
    );
  }

처음 마운트할 때 ReactCSSTransitionGroupexample-appear CSS 클래스를 받고 그 다음에 example-appear-active CSS 클래스가 추가됩니다.

.example-appear {
  opacity: 0.01;
}

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

처음 마운트할 때, ReactCSSTransitionGroup의 모든 자식은 appear하지만 enter하지 않습니다. 하지만, 존재하는 ReactCSSTransitionGroup에 추가되는 모든 자식은 enter하지만 appear하지 않습니다.

주의:

transitionAppear prop은 버전 0.13에서 ReactCSSTransitionGroup에 추가되었습니다. 하위 호환성을 생각해서, 기본 값은 false로 설정되어 있습니다.

커스텀 클래스 #

트렌지션의 각 단계에서 커스텀 클래스 이름을 사용할 수도 있습니다. transitionName에 문자열을 넘기는 대신 enter, leave 같은 클래스 이름의 객체나 enter, enter-active, leave-active, leave같은 클래스 이름의 객체를 넘길 수 있습니다. enter, leave 클래스만 있다면, enter-active, leave-active 클래스는 클래스 이름 뒤에 '-active'를 붙여서 정할 수 있습니다. 커스텀 클래스를 사용한 예제입니다.

  ...
  <ReactCSSTransitionGroup
    transitionName={ {
      enter: 'enter',
      enterActive: 'enterActive',
      leave: 'leave',
      leaveActive: 'leaveActive',
      appear: 'appear',
      appearActive: 'appearActive'
    } }>
    {item}
  </ReactCSSTransitionGroup>

  <ReactCSSTransitionGroup
    transitionName={ {
      enter: 'enter',
      leave: 'leave',
      appear: 'appear'
    } }>
    {item2}
  </ReactCSSTransitionGroup>
  ...

애니메이션 그룹이 작동하려면 마운트가 필요 #

자식들에게 트랜지션을 적용하려면 ReactCSSTransitionGroup은 이미 DOM에 마운트되어 있거나 prop transitionAppeartrue로 설정되어야만 합니다. 예를 들어, 밑의 코드는 동작하지 않을 것입니다. 왜냐하면 ReactCSSTransitionGroup 안에서 새 아이템을 마운트하는 대신 새 아이템과 같이 ReactCSSTransitionGroup를 마운트했기 때문입니다. 이 것을 위에 있는 시작하기 항목과 비교해보세요.

  render: function() {
    var items = this.state.items.map(function(item, i) {
      return (
        <div key={item} onClick={this.handleRemove.bind(this, i)}>
          <ReactCSSTransitionGroup transitionName="example">
            {item}
          </ReactCSSTransitionGroup>
        </div>
      );
    }, this);
    return (
      <div>
        <button onClick={this.handleAdd}>Add Item</button>
        {items}
      </div>
    );
  }

아이템 하나이거나 없을 때의 애니메이션 #

위의 예제에서 ReactCSSTransitionGroup에 아이템 목록을 렌더했지만, ReactCSSTransitionGroup의 자식은 하나이거나 없을 수도 있습니다. 이는 한 엘리먼트가 들어오고 나가는 것의 애니메이션을 가능하게 합니다. 비슷하게, 현재 엘리먼트가 나가는 동안 새 앨리먼트의 애니메이션을 하면, 새 엘리먼트가 현재 엘리먼트를 교체하는 애니메이션을 만들 수 있습니다. 예를 들어 이렇게 간단한 이미지 회전 베너(carousel)를 구현할 수 있습니다.

var ReactCSSTransitionGroup = require('react-addons-css-transition-group');

var ImageCarousel = React.createClass({
  propTypes: {
    imageSrc: React.PropTypes.string.isRequired
  },
  render: function() {
    return (
      <div>
        <ReactCSSTransitionGroup transitionName="carousel" transitionEnterTimeout={300} transitionLeaveTimeout={300}>
          <img src={this.props.imageSrc} key={this.props.imageSrc} />
        </ReactCSSTransitionGroup>
      </div>
    );
  }
});

애니메이션 비활성화 #

원한다면 enterleave 애니메이션을 비활성화 할 수 있습니다. 예를 들어, enter 애니메이션만 필요하고 leave 애니메이션은 필요없지만, ReactCSSTransitionGroup이 DOM 노드를 없애기 전 애니메이션이 완료되길 기다리고 있는 경우에 사용할 수 있습니다. ReactCSSTransitionGrouptransitionEnter={false}transitionLeave={false} props를 추가하면 그 애니메이션을 비활성화 할 수 있습니다.

주의:

ReactCSSTransitionGroup를 사용할 때는, 트랜지션이 종료되었을 때나 애니메이션 근처에서 더 복잡한 로직을 실행할 때 컴포넌트에 통지할 방법이 없습니다. 보다 세밀하게 제어하고 싶다면, 커스텀 트랜지션에 필요한 훅을 제공하는 저수준 ReactTransitionGroup API를 이용할 수 있습니다.

저수준 API: ReactTransitionGroup #

ReactTransitionGroup은 애니메이션의 기초입니다. 이는 require('react-addons-transition-group')으로 접근할 수 있습니다. 위의 예제처럼 자식들이 선언적으로 여기에 추가되거나 삭제되는 경우, 특별한 훅이 이 생명주기에서 호출됩니다.

componentWillAppear(callback) #

이미 있는 TransitionGroup에 컴포넌트를 추가할 때 호출되는 componentDidMount()와 같이 호출됩니다. 이는 callback이 호출될 때까지 다른 애니메이션을 막습니다. TransitionGroup의 최초 렌더에서만 호출됩니다.

componentDidAppear() #

이는 componentWillAppear에 넘겨졌던 callback 함수가 호출된 다음에 호출됩니다.

componentWillEnter(callback) #

이미 있는 TransitionGroup에 컴포넌트를 추가할 때 호출되는 componentDidMount()와 같이 호출됩니다. 이는 callback이 호출될 때까지 다른 애니메이션을 막습니다. TransitionGroup의 최조 렌더에서는 불려지지 않습니다.

componentDidEnter() #

이는 componentWillEnter에 넘겨주었던 callback 함수가 호출된 다음에 호출됩니다.

componentWillLeave(callback) #

이는 ReactTransitionGroup에서 자식이 제거되었을 때 호출됩니다. 자식이 제거되었다고 해도 ReactTransitionGroupcallback이 호출될 때까지 DOM에 자식을 남겨둡니다.

componentDidLeave() #

이는 willLeave callback이 호출될 때 호출됩니다. (componentWillUnmount와 같은 타이밍)

다른 컴포넌트 렌더하기 #

기본적으로 ReactTransitionGroupspan으로 렌더합니다. component prop으로 이 행동을 바꿀 수 있습니다. 예를 들어, <ul>을 렌더하고 싶다면 이렇게 하면 됩니다.

<ReactTransitionGroup component="ul">
  ...
</ReactTransitionGroup>

React가 렌더할 수 있는 DOM 컴포넌트는 전부 사용할 수 있습니다. 하지만 component가 DOM 컴포넌트일 필요는 없습니다. React 컴포넌트라면 무엇이든 넣을 수 있습니다. 직접 구현한 컴포넌트여도 됩니다! 그냥 component={List}를 적으면 컴포넌트는 this.props.children로 받을 수 있습니다.

사용자 정의를 포함한 어떤 프로퍼티도 렌더된 컴포넌트의 프로퍼티가 됩니다. 예를 들어, <ul>에 CSS 클래스를 넣어서 렌더하려면 이렇게 하면 됩니다.

<ReactTransitionGroup component="ul" className="animated-list">
  ...
</ReactTransitionGroup>