react-motion

by chenglou

chenglou /react-motion

A spring that solves your animation problems.

18.9K Stars 1.0K Forks Last release: almost 3 years ago (v0.5.2) MIT License 809 Commits 24 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

React-Motion

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

import {Motion, spring} from 'react-motion';
// In your render...

  {value => 
{value.x}
}

Animate a counter from

0
to
10
. For more advanced usage, see below.

Install

  • Npm:

    npm install --save react-motion
  • Bower: do not install with

    bower install react-motion
    , it won't work. Use

    bower install --save https://unpkg.com/react-motion/bower.zip
    . Or in
    bower.json
    :
    json
    {
    "dependencies": {
    "react-motion": "https://unpkg.com/react-motion/bower.zip"
    }
    }
    
    then include as
    html
    
    
  • 1998 Script Tag:

    html
    
    (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
  • With hot reloading (slow, development version): run
    npm start
    .
  • Without hot reloading (faster, production version): run
    npm run build-demos
    and open the static
    demos/demo_name/index.html
    file directly. Don't forget to use production mode when testing your animation's performance!

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: -

spring
-
Motion
-
StaggeredMotion
-
TransitionMotion
-
presets

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".
  • val
    : the value.
  • config
    : optional, for further adjustments. Possible fields:
    • stiffness
      : optional, defaults to
      170
      .
    • damping
      : optional, defaults to
      26
      .
    • precision
      : optional, defaults to
      0.01
      . Specifies both the rounding of the interpolated value and the speed (internal).

It's normal not to feel how stiffness and damping affect your spring; use Spring Parameters Chooser to get a feeling. Usually, you'd just use the list of tasteful stiffness/damping presets below.

- 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

  {interpolatingStyle => 
}

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:
  • an
    OpaqueConfig
    returned from
    spring(x)
    : interpolate to
    x
    .
  • a
    number
    x
    : jump to
    x
    , do not interpolate.
- defaultStyle?: PlainStyle

Optional. The

PlainStyle
type maps to
number
s. 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.

  • interpolatedStyle
    : the interpolated style object passed back to you. E.g. if you gave
    style={{x: spring(10), y: spring(20)}}
    , you'll receive as
    interpolatedStyle
    , at a certain time,
    {x: 5.2, y: 12.1}
    , which you can then apply on your
    div
    or something else.
  • Return: must return one React element to render.

- 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

 prevInterpolatedStyles.map((_, i) => {
    return i === 0
      ? {h: spring(100)}
      : {h: spring(prevInterpolatedStyles[i - 1].h)}
  })}>
  {interpolatingStyles =>
    
{interpolatingStyles.map((style, i) =>
) }
}

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"!

  • previousInterpolatedStyles
    : the previously interpolating (array of) styles (
    undefined
    at first render, unless
    defaultStyles
    is provided).
  • Return: must return an array of

    Style
    s containing the destination values, e.g.
    [{x: spring(10)}, {x: spring(20)}]
    .
- 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 ( ({ 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.

{interpolatedStyles.map(config => { return
})}
} ); }, });

Props

First, two type definitions to ease the comprehension.

  • TransitionStyle
    : an object of the format
    {key: string, data?: any, style: Style}
    .
    • key
      : required. The ID that
      TransitionMotion
      uses to track which configuration is which across renders, even when things are reordered. Typically reused as the component
      key
      when you map over the interpolated styles.
    • data
      : optional. Anything you'd like to carry along. This is so that when the previous section example's
      c
      disappears, you still get to access
      c
      's related data, such as the text to display along with it.
    • style
      : required. The actual starting style configuration, similar to what you provide for
      Motion
      's
      style
      . Maps keys to either a number or an
      OpaqueConfig
      returned by
      spring()
      .
  • TransitionPlainStyle
    : similar to above, except the
    style
    field's value is of type
    PlainStyle
    , aka an object that maps to numbers.
- styles: Array<TransitionStyle> | (previousInterpolatedStyles: ?Array<TransitionPlainStyle>) => Array<TransitionStyle>

Required. Accepts either:

  • an array of

    TransitionStyle
    configs, e.g.
    [{key: 'a', style: {x: spring(0)}}, {key: 'b', style: {x: spring(10)}}]
    .
  • a function similar to

    StaggeredMotion
    , taking the previously interpolating styles (
    undefined
    at first call, unless
    defaultStyles
    is provided), and returning the previously mentioned array of configs. You can do staggered mounting animation with this.
- 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.
  • styleThatLeft
    : the e.g.
    {key: ..., data: ..., style: ...}
    object from the
    styles
    array, identified by
    key
    , that was present during a previous render, and that is now absent, thus triggering the call to
    willLeave
    . Note that the style property is exactly what you passed in
    styles
    , and is not interpolated. For example, if you passed a spring for
    x
    you will receive an object like
    {x: {stiffness, damping, val, precision}}
    .
  • Return:

    null
    to indicate you want the
    TransitionStyle
    gone immediately. A
    Style
    object to indicate you want to reach transition to the specified value(s) before killing the
    TransitionStyle
    .
- didLeave?: (styleThatLeft:
{key: string, data?: any}
) => void

Optional. Defaults to

() => {}
.
  • styleThatLeft
    : the
    {key:..., data:...}
    that was removed after the finished transition.
- 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}
.
  • styleThatEntered
    : similar to
    willLeave
    's, except the
    TransitionStyle
    represents the object whose
    key
    value was absent during the last
    render
    , and that is now present.
  • Return: a

    defaultStyle
    -like
    PlainStyle
    configuration, e.g.
    {x: 0, y: 0}
    , that serves as the starting values of the animation. Under this light, the default provided means "a style config that has the same starting values as the destination values".

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

  • How do I set the duration of my animation?

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.

  • How do I unmount the
    TransitionMotion
    container itself?

You don't. Unless you put it in another

TransitionMotion
...
  • How do I do staggering/chained animation where items animate in one after another?

See

StaggeredMotion

  • My
    ref
    doesn't work in the children function.

React string refs won't work:

{currentValue => 
}

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

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.