Home

Awesome

React-Motion

Build Status npm version Bower version react-motion channel on discord

import {Motion, spring} from 'react-motion';
// In your render...
<Motion defaultStyle={{x: 0}} style={{x: spring(10)}}>
  {value => <div>{value.x}</div>}
</Motion>

Animate a counter from 0 to 10. For more advanced usage, see below.

Install

{
  "dependencies": {
    "react-motion": "https://unpkg.com/react-motion/bower.zip"
  }
}

then include as

<script src="bower_components/react-motion/build/react-motion.js"></script>
<script src="https://unpkg.com/react-motion/build/react-motion.js"></script>
(Module exposed as `ReactMotion`)

Works with React-Native v0.18+.

Demos

Check the wiki for more!

Try the Demos Locally

git clone https://github.com/chenglou/react-motion.git
cd react-motion
npm install

To build the repo yourself: npm run prepublish.

What does this library try to solve?

My React-Europe talk

For 95% of use-cases of animating components, we don't have to resort to using hard-coded easing curves and duration. Set up a stiffness and damping for your UI element, and let the magic of physics take care of the rest. This way, you don't have to worry about petty situations such as interrupted animation behavior. It also greatly simplifies the API.

This library also provides an alternative, more powerful API for React's TransitionGroup.

API

Exports:

Here's the well-annotated public Flow type definition file (you don't have to use Flow with React-motion, but the types help document the API below).

P.S. using TypeScript? Here are the React-motion TypeScript definitions!


Helpers

- spring: (val: number, config?: SpringHelperConfig) => OpaqueConfig

Used in conjunction with the components below. Specifies the how to animate to the destination value, e.g. spring(10, {stiffness: 120, damping: 17}) means "animate to value 10, with a spring of stiffness 120 and damping 17".

- Presets for {stiffness, damping}

Commonly used spring configurations used like so: spring(10, presets.wobbly) or spring(20, {...presets.gentle, precision: 0.1}). See here.


<Motion />

Usage

<Motion defaultStyle={{x: 0}} style={{x: spring(10)}}>
  {interpolatingStyle => <div style={interpolatingStyle} />}
</Motion>

Props

- style: Style

Required. The Style type is an object that maps to either a number or an OpaqueConfig returned by spring() above. Must keep the same keys throughout component's existence. The meaning of the values:

- defaultStyle?: PlainStyle

Optional. The PlainStyle type maps to numbers. Defaults to an object with the same keys as style above, whose values are the initial numbers you're interpolating on. Note that during subsequent renders, this prop is ignored. The values will interpolate from the current ones to the destination ones (specified by style).

- children: (interpolatedStyle: PlainStyle) => ReactElement

Required function.

- onRest?: () => void

Optional. The callback that fires when the animation comes to a rest.


<StaggeredMotion />

Animates a collection of (fixed length) items whose values depend on each other, creating a natural, springy, "staggering" effect like so. This is preferred over hard-coding a delay for an array of Motions to achieve a similar (but less natural-looking) effect.

Usage

<StaggeredMotion
  defaultStyles={[{h: 0}, {h: 0}, {h: 0}]}
  styles={prevInterpolatedStyles => prevInterpolatedStyles.map((_, i) => {
    return i === 0
      ? {h: spring(100)}
      : {h: spring(prevInterpolatedStyles[i - 1].h)}
  })}>
  {interpolatingStyles =>
    <div>
      {interpolatingStyles.map((style, i) =>
        <div key={i} style={{border: '1px solid', height: style.h}} />)
      }
    </div>
  }
</StaggeredMotion>

Aka "the current spring's destination value is the interpolating value of the previous spring". Imagine a spring dragging another. Physics, it works!

Props

- styles: (previousInterpolatedStyles: ?Array<PlainStyle>) => Array<Style>

Required function. Don't forget the "s"!

- defaultStyles?: Array<PlainStyle>

Optional. Similar to Motion's defaultStyle, but an array of them.

- children: (interpolatedStyles: Array<PlainStyle>) => ReactElement

Required function. Similar to Motion's children, but accepts the array of interpolated styles instead, e.g. [{x: 5}, {x: 6.4}, {x: 8.1}]

(No onRest for StaggeredMotion because we haven't found a good semantics for it yet. Voice your support in the issues section.)


<TransitionMotion />

Helps you to do mounting and unmounting animation.

Usage

You have items a, b, c, with their respective style configuration, given to TransitionMotion's styles. In its children function, you're passed the three interpolated styles as params; you map over them and produce three components. All is good.

During next render, you give only a and b, indicating that you want c gone, but that you'd like to animate it reaching value 0, before killing it for good.

Fortunately, TransitionMotion has kept c around and still passes it into the children function param. So when you're mapping over these three interpolated styles, you're still producing three components. It'll keep interpolating, while checking c's current value at every frame. Once c reaches the specified 0, TransitionMotion will remove it for good (from the interpolated styles passed to your children function).

This time, when mapping through the two remaining interpolated styles, you'll produce only two components. c is gone for real.

import createReactClass from 'create-react-class';

const Demo = createReactClass({
  getInitialState() {
    return {
      items: [{key: 'a', size: 10}, {key: 'b', size: 20}, {key: 'c', size: 30}],
    };
  },
  componentDidMount() {
    this.setState({
      items: [{key: 'a', size: 10}, {key: 'b', size: 20}], // remove c.
    });
  },
  willLeave() {
    // triggered when c's gone. Keeping c until its width/height reach 0.
    return {width: spring(0), height: spring(0)};
  },
  render() {
    return (
      <TransitionMotion
        willLeave={this.willLeave}
        styles={this.state.items.map(item => ({
          key: item.key,
          style: {width: item.size, height: item.size},
        }))}>
        {interpolatedStyles =>
          // first render: a, b, c. Second: still a, b, c! Only last one's a, b.
          <div>
            {interpolatedStyles.map(config => {
              return <div key={config.key} style={{...config.style, border: '1px solid'}} />
            })}
          </div>
        }
      </TransitionMotion>
    );
  },
});

Props

First, two type definitions to ease the comprehension.

- styles: Array<TransitionStyle> | (previousInterpolatedStyles: ?Array<TransitionPlainStyle>) => Array<TransitionStyle>

Required. Accepts either:

- defaultStyles?: Array<TransitionPlainStyle>

Optional. Similar to the other components' defaultStyle/defaultStyles.

- children: (interpolatedStyles: Array<TransitionPlainStyle>) => ReactElement

Required function. Similar to other two components' children. Receive back an array similar to what you provided for defaultStyles, only that each style object's number value represent the currently interpolating value.

- willLeave?: (styleThatLeft: TransitionStyle) => ?Style

Optional. Defaults to () => null. The magic sauce property.

- didLeave?: (styleThatLeft: {key: string, data?: any}) => void

Optional. Defaults to () => {}.

- willEnter?: (styleThatEntered: TransitionStyle) => PlainStyle

Optional. Defaults to styleThatEntered => stripStyle(styleThatEntered.style). Where stripStyle turns {x: spring(10), y: spring(20)} into {x: 10, y: 20}.

Note that willEnter and defaultStyles serve different purposes. willEnter only triggers when a previously inexistent TransitionStyle inside styles comes into the new render.

(No onRest for TransitionMotion because we haven't found a good semantics for it yet. Voice your support in the issues section.)


FAQ

Hard-coded duration goes against fluid interfaces. If your animation is interrupted mid-way, you'd get a weird completion animation if you hard-coded the time. That being said, in the demo section there's a great Spring Parameters Chooser for you to have a feel of what spring is appropriate, rather than guessing a duration in the dark.

You don't. Unless you put it in another TransitionMotion...

See StaggeredMotion

React string refs won't work:

<Motion style={...}>{currentValue => <div ref="stuff" />}</Motion>

This is how React works. Here's the callback ref solution.