javascriptreactjsreact-motion

How to use React TransitionMotion willEnter()


Using React Motion's TransitionMotion, I want to animate 1 or more boxes in and out. When a box enters the view, it's width and height should go from 0 pixels to 200 pixels and it's opacity should go from 0 to 1. When the box leaves the view, the reverse should happen (width/height = 0, opacity = 0)

I have tried to solve this problem here http://codepen.io/danijel/pen/RaboxO but my code is unable to transition the box in correctly. The box's style jumps immediately to a width/height of 200 pixels instead of transitioning in.

What is wrong with the code?

let Motion = ReactMotion.Motion
let TransitionMotion = ReactMotion.TransitionMotion
let spring = ReactMotion.spring
let presets = ReactMotion.presets

const Demo = React.createClass({
  getInitialState() {
    return {
      items: []
    }
  },
  componentDidMount() {

    let ctr = 0

    setInterval(() => {
      ctr++
      console.log(ctr)
      if (ctr % 2 == 0) {
        this.setState({
          items: [{key: 'b', width: 200, height: 200, opacity: 1}], // fade box in
        });
      } else {
        this.setState({
          items: [], // fade box out
        });
      }

    }, 1000)
  },
  willLeave() {
    // triggered when c's gone. Keeping c until its width/height reach 0.
    return {width: spring(0), height: spring(0), opacity: spring(0)};
  },

  willEnter() {
    return {width: 0, height: 0, opacity: 1};
  },

  render() {
    return (
      <TransitionMotion
        willEnter={this.willEnter}
        willLeave={this.willLeave}
        defaultStyles={this.state.items.map(item => ({
          key: item.key,
          style: {
            width: 0, 
            height: 0,
            opacity: 0
          },
        }))}
        styles={this.state.items.map(item => ({
          key: item.key,
          style: {
            width: item.width, 
            height: item.height,
            opacity: item.opacity
          },
        }))}
        >
        {interpolatedStyles =>
          <div>
            {interpolatedStyles.map(config => {
              return <div key={config.key} style={{...config.style, backgroundColor: 'yellow'}}>
                <div className="label">{config.style.width}</div>
              </div>
            })}
          </div>
        }
      </TransitionMotion>
    );
  },
});

ReactDOM.render(<Demo />, document.getElementById('app'));

Solution

  • As per the documentation of styles under the TransitionMotion section (and I don't claim to have understood all of it entirely :)):

    styles: ... an array of TransitionStyle ...

    The key thing to note here is that there are 2 types of style objects that this library deals with (or at least this TransitionMotion part of it) and it calls them TransitionStyle and TransitionPlainStyle.

    The previous values passed into styles attribute were of TransitionPlainStyle. Changing them to TransitionStyle magically starts animating the Enter sequence.

    You can read more about 2 different types mentioned above over here.

    styles={this.state.items.map(item => ({
      key: item.key,
      style: {
        width: spring(item.width), 
        height: spring(item.height),
        opacity: spring(item.opacity)
      }
    }))}
    

    Forked codepen demo.

    Again, I do not fully understand the inner workings of it just yet. I just know that your styles had to be changed in the above way to make it work.

    I will be happy if someone can educate me on this as well.

    Hope this helps.