最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to sequence animations in ReactJs - Stack Overflow

programmeradmin6浏览0评论

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/

发布评论

评论列表(0)

  1. 暂无评论