javascript - How to sequence animations in ReactJs - Stack Overflow

I'm having problems with plex animations, where one ponent has to finish animating before another.

I'm having problems with plex animations, where one ponent has to finish animating before another. In this example, I'm trying to fade out a ponent before another ponent is faded in. I can't use react-motion or any third party library, and can't rely on css transitions. Here is a working example highlighting the problem. Please note that the 'Editor' and 'Display' ponents aren't always of the same height.

Javascript

var Editor = React.createClass({
    render: function() {
        return <input type="text" defaultValue={this.props.name} />
    }
});

var Display = React.createClass({
    render: function() {
        return <div>{this.props.name}</div>;
    }
});

var Row = React.createClass({
        getInitialState: function() {
            return {
                isEditing: false
      }
    },
    updateRow: function() {
            this.setState({isEditing: !this.state.isEditing});
    },
    render: function() {
        return (
            <div className="row" onClick={this.updateRow}>
            <React.addons.TransitionGroup>
            {
            this.state.isEditing ?
                <Fade key="e"><Editor name={this.props.name}/></Fade> :
                <Fade key="d"><Display name={this.props.name}/></Fade>
            }
            </React.addons.TransitionGroup>
        </div>);
    }
});

var Table = React.createClass({
    render: function() {
        return (

            <div className="row" onClick={this.updateRow}>
              <Row name="One" />
                <Row name="Two" />
                <Row name="Three" />
                <Row name="Four" />
            </div>);
    }
});

var Fade = React.createClass({
  ponentWillEnter: function(callback) {
      var container = $(React.findDOMNode(this.refs.fade));
        container.animate({
        opacity:1
      }, callback);
  },
  ponentWillLeave: function(callback) {
        var container = $(React.findDOMNode(this.refs.fade));
        container.animate({
        opacity:0
      }, callback);
  },
  render: function() {
    return(<div className="fade" ref="fade">
        {this.props.children}
    </div>)
  }
});

ReactDOM.render(
    <Table />,
    document.getElementById('container')
);

CSS

.row {
  background-color: #c9c9c9;
  border-bottom: 1px solid #dedede;
  padding: 5px;
  color: gray;
  cursor:pointer;
}

HTML

<script src=".11.1/jquery.min.js"></script>

<script src=".js">
</script>


<div id="container">
</div>

I'm having problems with plex animations, where one ponent has to finish animating before another. In this example, I'm trying to fade out a ponent before another ponent is faded in. I can't use react-motion or any third party library, and can't rely on css transitions. Here is a working example highlighting the problem. Please note that the 'Editor' and 'Display' ponents aren't always of the same height.

Javascript

var Editor = React.createClass({
    render: function() {
        return <input type="text" defaultValue={this.props.name} />
    }
});

var Display = React.createClass({
    render: function() {
        return <div>{this.props.name}</div>;
    }
});

var Row = React.createClass({
        getInitialState: function() {
            return {
                isEditing: false
      }
    },
    updateRow: function() {
            this.setState({isEditing: !this.state.isEditing});
    },
    render: function() {
        return (
            <div className="row" onClick={this.updateRow}>
            <React.addons.TransitionGroup>
            {
            this.state.isEditing ?
                <Fade key="e"><Editor name={this.props.name}/></Fade> :
                <Fade key="d"><Display name={this.props.name}/></Fade>
            }
            </React.addons.TransitionGroup>
        </div>);
    }
});

var Table = React.createClass({
    render: function() {
        return (

            <div className="row" onClick={this.updateRow}>
              <Row name="One" />
                <Row name="Two" />
                <Row name="Three" />
                <Row name="Four" />
            </div>);
    }
});

var Fade = React.createClass({
  ponentWillEnter: function(callback) {
      var container = $(React.findDOMNode(this.refs.fade));
        container.animate({
        opacity:1
      }, callback);
  },
  ponentWillLeave: function(callback) {
        var container = $(React.findDOMNode(this.refs.fade));
        container.animate({
        opacity:0
      }, callback);
  },
  render: function() {
    return(<div className="fade" ref="fade">
        {this.props.children}
    </div>)
  }
});

ReactDOM.render(
    <Table />,
    document.getElementById('container')
);

CSS

.row {
  background-color: #c9c9c9;
  border-bottom: 1px solid #dedede;
  padding: 5px;
  color: gray;
  cursor:pointer;
}

HTML

<script src="https://ajax.googleapis./ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js">
</script>


<div id="container">
</div>
Share Improve this question edited Dec 12, 2015 at 8:01 user1036767 asked Dec 12, 2015 at 7:43 user1036767user1036767 271 silver badge7 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 8

Really, something like React Motion is the answer here, because it implements the functionality you want. However, it's certainly possible to implement it yourself. I'll overview a similar effect I created that demonstrates some techniques, then apply it to your specific code at the end.

The effect I implemented (fading children in or out one-at-a-time) was created by using a couple ponents:

  • StaggerIn - a ponent that fades its children in or out one-by-one, staggering the animations by props.delay milliseconds. This ponent is implemented with a TransitionGroup and wrapping the children in a StaggeringChild.
  • StaggeringChild - a ponent that implements React's TransitionGroup callbacks to do the actual animation.

When rendering, wrapping the children in a StaggerIn ponent triggers the effect:

if (this.state.active) {
  return (
    <StaggerIn delay={100}>
      <div key="one">One</div>
      <div key="two">Two</div>
      <div key="three">Three</div>
      <div key="four">Four</div>
      <div key="five">Five</div>
      <div key="six">Six</div>
      <div key="seven">Seven</div>
    </StaggerIn>
  );
} else {
  return <StaggerIn delay={100} />;
}

To make the staggering work, StaggerIn counts the number of children, and determines the appropriate delay by determining the index of each child (multiplied by the delay value):

var StaggerIn = React.createClass({
  render: function() {
    var childCount = React.Children.count(this.props.children);
    var children = React.Children.map(this.props.children, function(child, idx) {
      var inDelay = this.props.delay * idx;
      var outDelay = this.props.delay * (childCount - idx - 1);
      return (
        <StaggeringChild key={child.key}
                         animateInDelay={inDelay}
                         animateOutDelay={outDelay}>
          {child}
        </StaggeringChild>
      );
    }.bind(this));

    return (
      <React.addons.TransitionGroup>
        {children}
      </React.addons.TransitionGroup>
    );
  }
});

As mentioned, StaggerChild actually does the animation; here I'm using the TweenLite animation library in _animateIn and _animateOut, but jQuery animations and the like should work fine as well:

var StaggeringChild = React.createClass({
  getDefaultProps: function() {
    return {
      tag: "div"
    };
  },

  ponentWillAppear: function(callback) {
    this._animateIn(callback);
  },

  ponentWillEnter: function(callback) {
    this._animateIn(callback);
  },

  ponentWillLeave: function(callback) {
    this._animateOut(callback);
  },

  _animateIn(callback) {
    var el = React.findDOMNode(this);
    TweenLite.set(el, {opacity: 0});
    setTimeout(function() {
      console.log("timed in");
      TweenLite.to(el, 1, {opacity: 1}).play().eventCallback("onComplete", callback);
    }, this.props.animateInDelay);
  },

  _animateOut(callback) {
    var el = React.findDOMNode(this);
    setTimeout(function() {
      TweenLite.to(el, 1, {opacity: 0}).play().eventCallback("onComplete", callback);
    }, this.props.animateOutDelay);
  },

  render: function() {
    var Comp = this.props.tag;
    var { tag, animateInDelay, animateOutDelay, ...props } = this.props;

    return <Comp {...props}>{this.props.children}</Comp>;
  }
});

Here's a JSFiddle showing the pleted effect: http://jsfiddle/BinaryMuse/s2z0vmcn/


The key to making all this work is calculating the appropriate timeout value before you start to animate in or out. In your case, it's easy: you know you have exactly two items to animate, and you always want to fade out the one leaving before you fade in the one appearing.

First, let's specify a default property for a new prop called time that will specify how long the animation should take (since we'll need to know how long to wait):

var Fade = React.createClass({
  getDefaultProps: function() {
    return { time: 400 };
  },

  // ...
});

Next, we'll modify the animation methods so that leaving happens immediately, but appearing waits this.props.time milliseconds so that the leaving has time to finish first.

var Fade = React.createClass({
  // ...

  // no change to this function
  ponentWillLeave: function(callback) {
    var container = $(React.findDOMNode(this.refs.fade));
    container.animate({
      opacity:0
    }, this.props.time, callback);
  },

  ponentWillEnter: function(callback) {
    var container = $(React.findDOMNode(this.refs.fade));
    // hide element immediately
    container.css({opacity: 0});
    // wait until the leave animations finish before fading in
    setTimeout(function() {
      container.animate({
        opacity:1
      }, this.props.time, callback);
    }.bind(this), this.props.time);
  },

  // ...
});

With those changes, the item that's disappearing will animate out before the item that's appearing animates in. There's a bit of jumpiness because of the way the DOM works (crossfading elements is notoriously difficult) that will be left as an exercise to the reader. :)

Here's a working JSFiddle with the pleted code: https://jsfiddle/BinaryMuse/xfz3seyc/

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744205286a4563071.html

相关推荐

  • javascript - How to sequence animations in ReactJs - Stack Overflow

    I'm having problems with plex animations, where one ponent has to finish animating before another.

    9天前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信