Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-motion

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-motion - npm Package Compare versions

Comparing version 0.3.1 to 0.4.0

bower.json

13

HISTORY.md

@@ -7,2 +7,15 @@ Legend:

### 0.4.0 (January 26th 2016)
- [B] `spring` helper's format has changed from `spring(10, [120, 12])` to `spring(10, {stiffness: 120, damping: 12})`.
- [B] `style`, `styles` and `styles` of the three respective components now only accept either a number to interpolate, or a `spring` configuration to interpolate. Previously, it accepted (and ignored) random key/value pairs mixed in, such as `{x: spring(0), y: 'helloWorld'}`. `y` Doesn't belong there and should be placed elsewhere, e.g. directly on the (actual react) style of the component you're assigning the interpolating values on.
- [B] `TransitionMotion` got an all-around clearer API. See the [upgrade guide](https://github.com/chenglou/react-motion/wiki) and [README section](https://github.com/chenglou/react-motion/blob/05d76f5ec7e9722dbca0237a97c41267e297eb2c/README.md#transitionmotion-) for more.
- [B] `Motion`'s' `defaultStyle`, informally accepted the format `{x: spring(0)}`. This is now officially unsupported. The correct format has always been `{x: 0}`. Setting a default style of `{x: spring(whatever)}` did not make sense; the configuration only applies for a `style`, aka destination value. Same modification applies to `StaggeredMotion` and `TransitionMotion`'s `defaultStyles` & `willEnter`.
- [B] `TransitionMotion`'s `willEnter`/`willLeave`'s signature has changed.
- [B] The `reorderKeys` helper is no longer needed thanks to the changes to `TransitionMotion`. It's now removed.
- [B] React-Native specific build gone. RN 0.18+ uses the vanilla Npm React package, so there's no more need for us to export a wrapper.
- [F] Bunch of bugs gone: #225, #212, #179, #157, #90, #88.
- [I] `spring` has acquired a new field as part of the new signature: [precision tuning](https://github.com/chenglou/react-motion/blob/05d76f5ec7e9722dbca0237a97c41267e297eb2c/README.md#--spring-val-number-config-springhelperconfig--opaqueconfig)!
- [I] [Fully typed](https://github.com/chenglou/react-motion/blob/05d76f5ec7e9722dbca0237a97c41267e297eb2c/src/Types.js) via [Flow types](http://flowtype.org).
- [I] Performance improvements.
### 0.3.1 (October 14th 2015)

@@ -9,0 +22,0 @@ - [F] Handle `null` and `undefined` in `style`/`styles`. #181

106

lib/mergeDiff.js
// this function is allocation-less thanks to babel, which transforms the tail
// calls into loops
// babel transforms the tail calls into loops
'use strict';

@@ -9,3 +8,3 @@

exports['default'] = mergeDiff;
function mergeDiffArr(_x, _x2, _x3, _x4, _x5, _x6, _x7) {
function mergeDiffArr(_x, _x2, _x3, _x4, _x5, _x6) {
var _again = true;

@@ -16,8 +15,6 @@

arrB = _x2,
collB = _x3,
indexA = _x4,
indexB = _x5,
onRemove = _x6,
accum = _x7;
endA = endB = keyA = keyB = fill = fill = undefined;
indexA = _x3,
indexB = _x4,
onRemove = _x5,
accum = _x6;
_again = false;

@@ -27,20 +24,18 @@

var endB = indexB === arrB.length;
var keyA = arrA[indexA];
var keyB = arrB[indexB];
// const keyA = arrA[indexA].key;
// const keyB = arrB[indexB].key;
if (endA && endB) {
// returning null here, otherwise lint complains that we're not expecting
// a return value in subsequent calls. We know what we're doing.
return null;
return accum;
}
if (endA) {
accum[keyB] = collB[keyB];
accum.push(arrB[indexB]);
_x = arrA;
_x2 = arrB;
_x3 = collB;
_x4 = indexA;
_x5 = indexB + 1;
_x6 = onRemove;
_x7 = accum;
_x3 = indexA;
_x4 = indexB + 1;
_x5 = onRemove;
_x6 = accum;
_again = true;
endA = endB = undefined;
continue _function;

@@ -50,54 +45,63 @@ }

if (endB) {
var fill = onRemove(keyA);
var fill = onRemove(indexA, arrA[indexA]);
if (fill != null) {
accum[keyA] = fill;
accum.push(fill);
}
_x = arrA;
_x2 = arrB;
_x3 = collB;
_x4 = indexA + 1;
_x5 = indexB;
_x6 = onRemove;
_x7 = accum;
_x3 = indexA + 1;
_x4 = indexB;
_x5 = onRemove;
_x6 = accum;
_again = true;
endA = endB = fill = undefined;
continue _function;
}
if (keyA === keyB) {
accum[keyA] = collB[keyA];
if (arrA[indexA].key === arrB[indexB].key) {
accum.push(arrB[indexB]);
_x = arrA;
_x2 = arrB;
_x3 = collB;
_x4 = indexA + 1;
_x5 = indexB + 1;
_x6 = onRemove;
_x7 = accum;
_x3 = indexA + 1;
_x4 = indexB + 1;
_x5 = onRemove;
_x6 = accum;
_again = true;
endA = endB = fill = undefined;
continue _function;
}
if (!collB.hasOwnProperty(keyA)) {
var fill = onRemove(keyA);
// TODO: key search code
var found = false;
for (var i = indexB; i < arrB.length; i++) {
if (arrB[i].key === arrA[indexA].key) {
found = true;
break;
}
}
if (!found) {
var fill = onRemove(indexA, arrA[indexA]);
if (fill != null) {
accum[keyA] = fill;
accum.push(fill);
}
_x = arrA;
_x2 = arrB;
_x3 = collB;
_x4 = indexA + 1;
_x5 = indexB;
_x6 = onRemove;
_x7 = accum;
_x3 = indexA + 1;
_x4 = indexB;
_x5 = onRemove;
_x6 = accum;
_again = true;
endA = endB = fill = found = i = fill = undefined;
continue _function;
}
// key a != key b, key a (old) not found in new arr (arr b)
_x = arrA;
_x2 = arrB;
_x3 = collB;
_x4 = indexA + 1;
_x5 = indexB;
_x6 = onRemove;
_x7 = accum;
_x3 = indexA + 1;
_x4 = indexB;
_x5 = onRemove;
_x6 = accum;
_again = true;
endA = endB = fill = found = i = fill = undefined;
continue _function;

@@ -107,10 +111,6 @@ }

function mergeDiff(a, b, onRemove) {
var ret = {};
// if anyone can make this work without allocating the arrays here, we'll
// give you a medal
mergeDiffArr(Object.keys(a), Object.keys(b), b, 0, 0, onRemove, ret);
return ret;
function mergeDiff(prev, next, onRemove) {
return mergeDiffArr(prev, next, 0, 0, onRemove, []);
}
module.exports = exports['default'];

@@ -1,3 +0,1 @@

// [stiffness, damping]
"use strict";

@@ -7,7 +5,7 @@

exports["default"] = {
noWobble: [170, 26], // the default
gentle: [120, 14],
wobbly: [180, 12],
stiff: [210, 20]
noWobble: { stiffness: 170, damping: 26 }, // the default, if nothing provided
gentle: { stiffness: 120, damping: 14 },
wobbly: { stiffness: 180, damping: 12 },
stiff: { stiffness: 210, damping: 20 }
};
module.exports = exports["default"];

@@ -5,43 +5,28 @@ 'use strict';

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _interopRequire(obj) { return obj && obj.__esModule ? obj['default'] : obj; }
var _react = require('react');
var _Motion = require('./Motion');
var _react2 = _interopRequireDefault(_react);
exports.Motion = _interopRequire(_Motion);
var _components2 = require('./components');
var _StaggeredMotion = require('./StaggeredMotion');
var _components3 = _interopRequireDefault(_components2);
exports.StaggeredMotion = _interopRequire(_StaggeredMotion);
var _reorderKeys = require('./reorderKeys');
var _TransitionMotion = require('./TransitionMotion');
var _reorderKeys2 = _interopRequireDefault(_reorderKeys);
exports.TransitionMotion = _interopRequire(_TransitionMotion);
var _components = _components3['default'](_react2['default']);
var _spring = require('./spring');
var Spring = _components.Spring;
var TransitionSpring = _components.TransitionSpring;
var Motion = _components.Motion;
var StaggeredMotion = _components.StaggeredMotion;
var TransitionMotion = _components.TransitionMotion;
exports.Spring = Spring;
exports.TransitionSpring = TransitionSpring;
exports.Motion = Motion;
exports.StaggeredMotion = StaggeredMotion;
exports.TransitionMotion = TransitionMotion;
exports.spring = _interopRequire(_spring);
var _spring2 = require('./spring');
var _presets = require('./presets');
var _spring3 = _interopRequireDefault(_spring2);
exports.presets = _interopRequire(_presets);
exports.spring = _spring3['default'];
// deprecated, dummy warning function
var _presets2 = require('./presets');
var _reorderKeys = require('./reorderKeys');
var _presets3 = _interopRequireDefault(_presets2);
exports.presets = _presets3['default'];
var utils = {
reorderKeys: _reorderKeys2['default']
};
exports.utils = utils;
exports.reorderKeys = _interopRequire(_reorderKeys);

@@ -1,17 +0,17 @@

"use strict";
'use strict';
exports.__esModule = true;
exports["default"] = reorderKeys;
exports['default'] = reorderKeys;
function reorderKeys(obj, f) {
var newKeys = f(Object.keys(obj));
var ret = {};
for (var i = 0; i < newKeys.length; i++) {
var key = newKeys[i];
ret[key] = obj[key];
var hasWarned = false;
function reorderKeys() {
if (process.env.NODE_ENV === 'development') {
if (!hasWarned) {
hasWarned = true;
console.error('`reorderKeys` has been removed, since it is no longer needed for TransitionMotion\'s new styles array API.');
}
}
return ret;
}
module.exports = exports["default"];
module.exports = exports['default'];
'use strict';
exports.__esModule = true;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports['default'] = spring;

@@ -12,8 +15,10 @@

function spring(val) {
var config = arguments.length <= 1 || arguments[1] === undefined ? _presets2['default'].noWobble : arguments[1];
var defaultConfig = _extends({}, _presets2['default'].noWobble, {
precision: 0.01
});
return { val: val, config: config };
function spring(val, config) {
return _extends({}, defaultConfig, config, { val: val });
}
module.exports = exports['default'];

@@ -1,9 +0,14 @@

"use strict";
// stepper is used a lot. Saves allocation to return the same array wrapper.
// This is fine and danger-free against mutations because the callsite
// immediately destructures it and gets the numbers inside without passing the
'use strict';
exports.__esModule = true;
exports["default"] = stepper;
exports['default'] = stepper;
var errorMargin = 0.0001;
var reusedTuple = [];
function stepper(frameRate, x, v, destX, k, b) {
function stepper(secondPerFrame, x, v, destX, k, b, precision) {
// Spring stiffness, in kg / s^2

@@ -23,12 +28,22 @@

var newV = v + a * frameRate;
var newX = x + newV * frameRate;
var newV = v + a * secondPerFrame;
var newX = x + newV * secondPerFrame;
if (Math.abs(newV - v) < errorMargin && Math.abs(newX - x) < errorMargin) {
return [destX, 0];
// TODO: remove
if (x == null || v == null || destX == null || k == null || b == null || Number.isNaN(x) || Number.isNaN(v) || Number.isNaN(destX) || Number.isNaN(newV) || Number.isNaN(newX)) {
throw new Error('wtf');
}
return [newX, newV];
if (Math.abs(newV) < precision && Math.abs(newX - destX) < precision) {
reusedTuple[0] = destX;
reusedTuple[1] = 0;
return reusedTuple;
}
reusedTuple[0] = newX;
reusedTuple[1] = newV;
return reusedTuple;
}
module.exports = exports["default"];
module.exports = exports['default'];
// array reference around.
// turn {x: {val: 1, config: [1, 2]}, y: 2} generated by
// turn {x: {val: 1, stiffness: 1, damping: 2}, y: 2} generated by
// `{x: spring(1, [1, 2]), y: 2}` into {x: 1, y: 2}

@@ -16,3 +16,3 @@

}
ret[key] = style[key] == null || style[key].val == null ? style[key] : style[key].val;
ret[key] = typeof style[key] === 'number' ? style[key] : style[key].val;
}

@@ -19,0 +19,0 @@ return ret;

@@ -0,3 +1,27 @@

// === basic reused types ===
// type of the second parameter of `spring(val, config)` all fields are optional
"use strict";
exports.__esModule = true;
// the object returned by `spring(value, yourConfig)`. Used internally only!
// your typical style object given in props. Maps to a number or a spring config
// the interpolating style object, with the same keys as the above Style object,
// with the values mapped to numbers, naturally
// internal velocity object. Similar to PlainStyle, but whose numbers represent
// speed. Might be exposed one day.
// === Motion ===
// === StaggeredMotion ===
// === TransitionMotion ===
// actual style you're passing
exports.__esModule = true;
// unique ID to identify component across render animations
// optional data you want to carry along the style, e.g. itemText
// same as TransitionStyle, passed as argument to style/children function
{
"name": "react-motion",
"version": "0.3.1",
"version": "0.4.0",
"description": "A spring that solves your animation problems.",

@@ -13,12 +13,10 @@ "main": "lib/react-motion.js",

"babel-core": "^5.6.18",
"babel-eslint": "^3.1.23",
"babel-eslint": "^4.1.3",
"babel-loader": "^5.3.1",
"codemirror": "^5.5.0",
"colors-mini": "^1.0.2",
"css-loader": "^0.19.0",
"diff": "^2.1.3",
"eslint": "^0.24.1",
"eslint-config-airbnb": "0.0.6",
"eslint-loader": "^0.14.1",
"eslint-plugin-react": "^2.7.0",
"eslint": "^1.6.0",
"eslint-config-airbnb": "0.1.0",
"eslint-loader": "^1.1.0",
"eslint-plugin-react": "^3.5.1",
"inject-loader": "^2.0.1",

@@ -30,9 +28,11 @@ "isparta-loader": "^0.2.0",

"karma-jasmine": "^0.3.6",
"karma-jasmine-diff-reporter": "^0.3.2",
"karma-phantomjs-launcher": "^0.2.0",
"karma-webpack": "^1.7.0",
"lodash.isequal": "^3.0.4",
"lodash.range": "^3.0.1",
"phantomjs": "^1.9.17",
"react": "^0.14.0",
"react-codemirror": "^0.1.2",
"react": ">=0.14.3",
"react-addons-test-utils": "^0.14.6",
"react-codemirror": ">=0.1.2",
"react-dom": ">=0.14.3",
"react-hot-loader": "^1.2.8",

@@ -47,3 +47,3 @@ "style-loader": "^0.12.4",

"lint": "eslint --ext .js,.jsx .",
"prerelease": "babel src --out-dir lib && babel native/non-compiled.js > native/index.js && webpack --config webpack.prod.config.js",
"prerelease": "rm lib/* && babel src --out-dir lib && webpack --config webpack.prod.config.js",
"test": "karma start ./karma.conf.js --single-run",

@@ -50,0 +50,0 @@ "test:travis": "karma start ./karma.conf.js --single-run",

@@ -6,3 +6,3 @@ # React-Motion

[![Bower version](https://badge.fury.io/bo/react-motion.svg)](http://badge.fury.io/bo/react-motion)
[![react-motion channel on slack](https://img.shields.io/badge/slack-react--motion%40reactiflux-61DAAA.svg?style=flat)](http://reactiflux.herokuapp.com)
[![react-motion channel on discord](https://img.shields.io/badge/discord-react--motion%40reactiflux-738bd7.svg?style=flat)](https://discordapp.com/invite/0ZcbPKXt5bYzmcI0)

@@ -19,21 +19,38 @@ ```jsx

## Install
### Install
Npm:
`npm install react-motion`
- Npm: `npm install --save react-motion`
Bower:
`bower install react-motion`
- Bower: `bower install --save https://npmcdn.com/react-motion/bower.zip`. Or in `bower.json`:
```json
{
"dependencies": {
"react-motion": "https://npmcdn.com/react-motion/bower.zip"
}
}
```
then include as
```html
<script src="bower_components/react-motion/build/react-motion.js"></script>
```
1998 Script Tag:
`<script src="path/to/react-motion/build/react-motion.js"></script>`
- 1998 Script Tag:
```html
<script src="https://npmcdn.com/react-motion/build/react-motion.js"></script>
(Module exposed as `ReactMotion`)
```
Or build it yourself from the repo: `git clone https://github.com/chenglou/react-motion.git && cd react-motion && npm install && npm run prerelease`
- Build it yourself from the repo:
```sh
git clone https://github.com/chenglou/react-motion.git
cd react-motion
npm install
npm run prerelease
```
**For React-native**, instead of `require('react-motion')`, do `require('react-motion/native')`.
To try the demos locally with hot reloading, run `npm start`. **Don't forget to compile for production when you test your animation's performance!**
_(P.S. Don't forget to compile for production when you test your animation's performance!)_
**For React-native**: previous react-motion v0.3.* supported a dedicated build for RN, which has now been made obsolete since RN v0.18. This library should Just Work under the new React-Native 0.18+.
## Demos
### Demos
- [Simple Transition](http://chenglou.github.io/react-motion/demos/demo0-simple-transition)

@@ -48,2 +65,4 @@ - [Chat Heads](http://chenglou.github.io/react-motion/demos/demo1-chat-heads)

[Check the wiki for more!](https://github.com/chenglou/react-motion/wiki/Gallery-of-third-party-React-Motion-demos)
## What does this library try to solve?

@@ -58,53 +77,80 @@

## API
The library exports `Motion`, `StaggeredMotion`, `TransitionMotion`, `presets`, `spring` and `utils`.
#### `spring: number -> ?[stiffness, damping] -> ConfigurationObject`
(**Note**: not the `Spring` component in version <0.3.0.)
The pervasive helper used to specify the how to animate to the destination value, e.g. `spring(10, [120, 17])` returns an opaque configuration that describes "an animation to the value 10, with a physics spring's stiffness of 120 and damping of 17". `spring(10)` without the spring configuration array defaults to `[170, 26]`. See below for more usage and see [here](#presets) for a list of convenient configurations the library exports.
**Coming from 0.3.* to 0.4.0? [Here](https://github.com/chenglou/react-motion/wiki)'s the upgrade guide.**
### &lt;Motion />
Props:
Exports:
- `spring`.
- `Motion`
- `StaggeredMotion`
- `TransitionMotion`
- `presets`
#### `defaultStyle: ?Object`
Optional. The value when the component first renders (ignored in subsequent renders). Accepts an object of arbitrary keys, mapping to initial values you want to animate, e.g. `{x: 0, y: 10}`.
[Here](https://github.com/chenglou/react-motion/blob/783c52ad777fb4558a7561bd10d917ada9b8c58d/src/Types.js)'s the well-annotated public [Flow type](http://flowtype.org) definition file (you don't have to use Flow with React-motion, but the types help document the API below).
#### `style: Object`
Required. Must have the same keys throughout component's existence. Must have the same keys as `defaultStyle` (if provided). Similar to `defaultStyle`, but asks for a `spring` configuration as the destination value: `{x: spring(10), y: spring(20, [120, 17])}`.
---
**If a plain number is provided rather than a `spring` config**, instead of giving an interpolated value in the children function param below, we'll jump straight to that number value.
### Helpers
#### `children: Object -> ?ReactElement`
Required **function**, which is passed an interpolated style object, e.g. `{x: 5.2, y: 12.1}`. Must returns a React element to render.
##### - 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](http://chenglou.github.io/react-motion/demos/demo5-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](https://github.com/chenglou/react-motion/blob/1cf9ef4a95000ef8b173fdb5a74e9e54597b8e33/src/presets.js).
---
### &lt;Motion />
#### Usage
```jsx
<Motion defaultStyle={{x: 0}} style={{x: spring(10, [120, 17])}}>
{interpolatedStyle => <div>{interpolatedStyle.x}</div>}
<Motion defaultStyle={{x: 0}} style={{x: spring(10)}}>
{interpolatingStyle => <div style={interpolatingStyle} />}
</Motion>
```
### &lt;StaggeredMotion />
When you want to animate a list of items, you can certainly create an array of `Motion`s and animate each. However, you often want to "stagger" them, i.e. animate items in one after another with a delay. Hard-coding this duration goes against the very purpose of spring physics. Instead, here's a natural, physics-based alternative, where "the destination position of an item depends on the current position of another".
#### Props
#### `defaultStyles: ?Array<Object>`
Optional. Similar to `Motion`'s `defaultStyle`, except an array of styles.
##### - style: Style
#### `styles: ?Array<Object> -> Array<Object>`
Required **function**. Takes as argument the previous array of styles (which is `undefined` at first render, unless `defaultStyles` is provided!). Return the array of styles containing the destination values.
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:
#### `children: Array<Object> -> ?ReactElement`
A 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}]`
- 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.
---
### &lt;StaggeredMotion />
Animates a collection of items whose values depend on each other, creating a natural, springy, "staggering" effect [like so](http://chenglou.github.io/react-motion/demos/demo1-chat-heads). This is preferred over hard-coding a delay for an array of `Motions` to achieve a similar (but less natural-looking) effect.
#### Usage
```jsx
<StaggeredMotion
defaultStyles={[{x: 0}, {x: 10}, {x: 20}]}
styles={prevStyles => prevStyles.map((_, i) => {
defaultStyles={[{h: 0}, {h: 0}, {h: 0}]}
styles={prevInterpolatedStyles => prevInterpolatedStyles.map((_, i) => {
return i === 0
? {x: spring(this.state.mouseX)} // first item follows mouse's x position
: prevStyles[i - 1]; // item i follow the position of the item before it, creating a natural staggering spring
? {h: spring(100)}
: {h: spring(prevInterpolatedStyles[i - 1].h)}
})}>
{interpolatedStyles =>
{interpolatingStyles =>
<div>
{interpolatedStyles.map((style, i) =>
<div key={i} style={{left: style.x}} />
)}
{interpolatingStyles.map((style, i) =>
<div key={i} style={{border: '1px solid', height: style.h}} />)
}
</div>

@@ -115,36 +161,33 @@ }

### &lt;TransitionMotion />
**The magic component that helps you to do mounting and unmounting animation.** Unlike React's `TransitionGroup`, instead of retaining a few items in a list when they disappear, `TransitionMotion` diffs on the shape of its `styles` object prop.
Aka "the current spring's destination value is the interpolating value of the previous spring". Imagine a spring dragging another. Physics, it works!
**The general idea**
#### Props
Let `TransitionMotion`'s `styles` be `{myKey1: {x: spring(30)}, myKey2: {x: spring(20)}}`. The interpolated styles passed to the `children` function, after a moment, would be `{myKey1: {x: 15.1}, myKey2: {x: 8.2}}`.
##### - styles: (previousInterpolatedStyles: ?Array&lt;PlainStyle>) => Array&lt;Style>
Required **function**. **Don't forget the "s"**!
A few renders later, you kill `myKey1` and its style config, i.e. pass the new `styles` as `{myKey2: {x: spring(20)}}`. TransitionMotion detects a missing key, but **retains** the key in the interpolated values as `{myKey1: ..., myKey2: ...}`.
- `previousInterpolatedStyles`: the previously interpolating (array of) styles (`undefined` at first render, unless `defaultStyles` is provided).
This is when `TransitionMotion` calls the prop `willLeave` that you provide, passing `myKey2` as argument. You're asked to return a final style config (for example, `{x: spring(50)}`) for `myKey1`, representing the style values that, when `interpolatedStyles.myKey1` reaches them, allows `TransitionMotion` to truly kill `myKey1` and its style config from the interpolated styles.
- Return: must return an array of `Style`s containing the destination values, e.g. `[{x: spring(10)}, {x: spring(20)}]`.
In summary: `styles` is `{k1: {x: spring(30)}, k2: {x: spring(20)}}`. Next render, `styles` is `{k2: {x: spring(20)}}`. The interpolated styles passed to children aren't affected, but remember that the `k2: configReturnedFromWillLeave` (say, `{x: spring(50)}`) part doesn't exist in the actual `styles` anymore. Moments later, interpolated styles reach `{k1: {x: 50}, k2: {x: 19.2}}`; it then re-renders, kills `k1` and become `{k2: {x: 19.2}}`. All this time, you're mapping over the interpolate styles and rendering two items, until the last render.
##### - defaultStyles?: Array&lt;PlainStyle>
Optional. Similar to `Motion`'s `defaultStyle`, but an array of them.
Similar but simpler logic for `willEnter`.
##### - children: (interpolatedStyles: Array&lt;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}]`
#### `defaultStyles: ?Object<string, Object>`
Optional. Accepts an object of the format `{myKey1: styleObject, myKey2: styleObject}` where each `styleObject` is similar to `Motion`'s `defaultStyle`. The keys must be unique **non-number** IDs (number keys in JS object screws with keys enumeration order, which is important when you map over it in `children` function).
---
#### `styles: Object | (?Object -> Object)`
Required. Accepts an object similar to `defaultStyles`, but where `styleObject` has `spring` configurations: `{myKey1: {x: spring(10)}, myKey2: {y: spring(20)}}`. Alternatively, also accepts a function which takes a `prevStyles` parameter (just like `StaggeredMotion`; you can do staggered unmounting animation!), and returns the destination styles.
### &lt;TransitionMotion />
**Helps you to do mounting and unmounting animation**.
#### `willEnter: (string, Object, Object, Object, Object) -> Object`
__Not a very helpful type definition...__
Optional. Pass a function that takes the arguments `(keyFromStylesThatJustEntered, correspondingStyleOfKey, styles, currentInterpolatedStyle, currentSpeed)`, and that returns a style object similar to a `defaultStyle`.
#### Usage
Defaults to a function that returns `correspondingStyleOfKey`, in this case `{x: spring(20)}`.
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.
#### `willLeave: (string, Object, Object, Object, Object) -> Object`
Optional. Pass a function that takes the arguments `keyThatJustLeft, correspondingStyleOfKey, styles, currentInterpolatedStyle, currentSpeed)` and that return a style object containing some `spring(...)` as the destination configuration.
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.
Optional, defaults to `correspondingStyleOfKey`, i.e. immediately killing the key from the interpolated values.
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).
#### `children: Object -> ?ReactElement`
A required **function**. Similar to `Motion`'s `children`, but accepts the object of interpolated styles instead.
This time, when mapping through the two remaining interpolated styles, you'll produce only two components. `c` is gone for real.

@@ -155,59 +198,27 @@ ```jsx

return {
blocks: {
a: 'I am a',
b: 'I am b',
c: 'I am c',
},
items: [{key: 'a', size: 10}, {key: 'b', size: 20}, {key: 'c', size: 30}],
};
},
getStyles() {
let configs = {};
Object.keys(this.state.blocks).forEach(key => {
configs[key] = {
opacity: spring(1),
text: this.state.blocks[key], // not interpolated
};
componentDidMount() {
this.setState({
items: [{key: 'a', size: 10}, {key: 'b', size: 20}], // remove c.
});
return configs;
},
// not used here! We don't add any new item
willEnter(key) {
return {
opacity: spring(0), // start at 0, gradually expand
text: this.state.blocks[key], // this is really just carried around so
// that interpolated values can still access the text when the key is gone
// from actual `styles`
};
willLeave() {
// triggered when c's gone. Keeping c until its width/height reach 0.
return {width: spring(0), height: spring(0)};
},
willLeave(key, style) {
return {
opacity: spring(0), // make opacity reach 0, after which we can kill the key
text: style.text,
};
},
handleClick(key) {
const {...newBlocks} = this.state.blocks;
delete newBlocks[key];
this.setState({blocks: newBlocks});
},
render() {
return (
<TransitionMotion
styles={this.getStyles()}
willEnter={this.willEnter}
willLeave={this.willLeave}>
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>
{Object.keys(interpolatedStyles).map(key => {
const {text, ...style} = interpolatedStyles[key];
return (
<div onClick={this.handleClick.bind(null, key)} style={style}>
{text}
</div>
);
{interpolatedStyles.map(config => {
return <div key={config.key} style={{...config.style, border: '1px solid'}} />
})}

@@ -222,19 +233,47 @@ </div>

### `presets`
Some tasteful, commonly used spring presets you can plug into your `style` like so: `spring(10, presets.wobbly)`. [See here](https://github.com/chenglou/react-motion/blob/043231a84e420ba1cc7f5b0ceb1753a6406d38f1/src/presets.js).
#### Props
### `utils`
Since `TransitionMotion` dictates `styles` to be an object, manipulating keys could be a little more tedious than manipulating arrays. Here are the common scenarios' solutions:
First, two type definitions to ease the comprehension.
- Insert item at the beginning: `{newKey: myConfigForThisKey, ...oldConfigs}`.
- Insert item at the end: `{...oldConfigs, newKey: myConfigForThisKey}`.
- Slice/splice/reverse/sort: this library exposes a `utils.reorderKeys` function.
- `TransitionStyle`: an object of the format `{key: any, data?: any, style: Style}`.
**Note**: object keys creation order is now guaranteed by the specs, except for integer keys, which follow ascending order and should not be used with `TransitionMotion`. Fortunately, you can just add a letter to your key to turn them into "true" strings.
- `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.
#### `reorderKeys: (Object, Function) -> Object`
`utils.reorderKeys({a: 1, b: 2}, (keysArray) => ['b', 'a']) // gives {b: 2, a: 1}`
- `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.
`Function` will receive, as arguments, the array of keys in `Object` and should return a new array of keys (with e.g. order changed and/or keys removed). `reorderKeys` will then return a new object of the same shape as `object`, but with the keys in the order `Function` dictated.
- `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&lt;TransitionStyle> | (previousInterpolatedStyles: ?Array&lt;TransitionPlainStyle>) => Array&lt;TransitionStyle>
Required. Accepts either:
- an array of `TransitionStyle` configs, e.g. `[{key: 1, style: {x: spring(0)}}, {key: 2, 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&lt;TransitionPlainStyle>
Optional. Similar to the other components' `defaultStyle`/`defaultStyles`.
##### - children: (interpolatedStyles: Array&lt;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: ..., value: ...}` 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`.
- 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`.
##### - willEnter?: (styleThatEntered: TransitionStyle) => PlainStyle
Optional. Defaults to `styleThatEntered => stripStyle(styleThatEntered.style)`. Where `stripStyle` turns `{a: spring(10), b: spring(20)}` into `{a: 10, b: 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. `{a: 0, b: 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.
---
## FAQ

@@ -244,3 +283,3 @@

[Hard-coded duration goes against fluid interfaces](https://twitter.com/andy_matuschak/status/566736015188963328). 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](http://chenglou.github.io/react-motion/demos/demo5-spring-parameters-chooser/) for you to have a feel of what spring is appropriate, rather than guessing a duration in the dark.
[Hard-coded duration goes against fluid interfaces](https://twitter.com/andy_matuschak/status/566736015188963328). 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](http://chenglou.github.io/react-motion/demos/demo5-spring-parameters-chooser) for you to have a feel of what spring is appropriate, rather than guessing a duration in the dark.

@@ -253,3 +292,3 @@ - How do I unmount the `TransitionMotion` container itself?

See [`StaggeredMotion`](#StaggeredMotion)
See [`StaggeredMotion`](#staggeredmotion-)

@@ -256,0 +295,0 @@ - My `ref` doesn't work in the children function.

/* @flow */
import type {Style, TransitionStyles} from './Types';
import type {TransitionStyle} from './Types';
// this function is allocation-less thanks to babel, which transforms the tail
// calls into loops
function mergeDiffArr(arrA, arrB, collB, indexA, indexB, onRemove, accum) {
// babel transforms the tail calls into loops
function mergeDiffArr(arrA, arrB, indexA, indexB, onRemove, accum): Array<TransitionStyle> {
const endA = indexA === arrA.length;
const endB = indexB === arrB.length;
const keyA = arrA[indexA];
const keyB = arrB[indexB];
// const keyA = arrA[indexA].key;
// const keyB = arrB[indexB].key;
if (endA && endB) {
// returning null here, otherwise lint complains that we're not expecting
// a return value in subsequent calls. We know what we're doing.
return null;
return accum;
}
if (endA) {
accum[keyB] = collB[keyB];
return mergeDiffArr(arrA, arrB, collB, indexA, indexB + 1, onRemove, accum);
accum.push(arrB[indexB]);
return mergeDiffArr(arrA, arrB, indexA, indexB + 1, onRemove, accum);
}
if (endB) {
let fill = onRemove(keyA);
const fill = onRemove(indexA, arrA[indexA]);
if (fill != null) {
accum[keyA] = fill;
accum.push(fill);
}
return mergeDiffArr(arrA, arrB, collB, indexA + 1, indexB, onRemove, accum);
return mergeDiffArr(arrA, arrB, indexA + 1, indexB, onRemove, accum);
}
if (keyA === keyB) {
accum[keyA] = collB[keyA];
return mergeDiffArr(arrA, arrB, collB, indexA + 1, indexB + 1, onRemove, accum);
if (arrA[indexA].key === arrB[indexB].key) {
accum.push(arrB[indexB]);
return mergeDiffArr(arrA, arrB, indexA + 1, indexB + 1, onRemove, accum);
}
if (!collB.hasOwnProperty(keyA)) {
let fill = onRemove(keyA);
// TODO: key search code
let found = false;
for (let i = indexB; i < arrB.length; i++) {
if (arrB[i].key === arrA[indexA].key) {
found = true;
break;
}
}
if (!found) {
const fill = onRemove(indexA, arrA[indexA]);
if (fill != null) {
accum[keyA] = fill;
accum.push(fill);
}
return mergeDiffArr(arrA, arrB, collB, indexA + 1, indexB, onRemove, accum);
return mergeDiffArr(arrA, arrB, indexA + 1, indexB, onRemove, accum);
}
return mergeDiffArr(arrA, arrB, collB, indexA + 1, indexB, onRemove, accum);
// key a != key b, key a (old) not found in new arr (arr b)
return mergeDiffArr(arrA, arrB, indexA + 1, indexB, onRemove, accum);
}
export default function mergeDiff(
a: Style,
b: Style,
onRemove: (key: string) => ?Style): TransitionStyles {
let ret = {};
// if anyone can make this work without allocating the arrays here, we'll
// give you a medal
mergeDiffArr(Object.keys(a), Object.keys(b), b, 0, 0, onRemove, ret);
return ret;
prev: Array<TransitionStyle>,
next: Array<TransitionStyle>,
onRemove: (prevIndex: number, prevStyleCell: TransitionStyle) => ?TransitionStyle
): Array<TransitionStyle> {
return mergeDiffArr(prev, next, 0, 0, onRemove, []);
}
/* @flow */
// [stiffness, damping]
export default {
noWobble: [170, 26], // the default
gentle: [120, 14],
wobbly: [180, 12],
stiff: [210, 20],
noWobble: {stiffness: 170, damping: 26}, // the default, if nothing provided
gentle: {stiffness: 120, damping: 14},
wobbly: {stiffness: 180, damping: 12},
stiff: {stiffness: 210, damping: 20},
};

@@ -1,10 +0,9 @@

import React from 'react';
import components from './components';
/* @flow */
export {default as Motion} from './Motion';
export {default as StaggeredMotion} from './StaggeredMotion';
export {default as TransitionMotion} from './TransitionMotion';
export {default as spring} from './spring';
export {default as presets} from './presets';
export const {Spring, TransitionSpring, Motion, StaggeredMotion, TransitionMotion} = components(React);
export spring from './spring';
export presets from './presets';
import reorderKeys from './reorderKeys';
export const utils = {
reorderKeys,
};
// deprecated, dummy warning function
export {default as reorderKeys} from './reorderKeys';
/* @flow */
export default function reorderKeys(
obj: Object,
f: (keys: Array<string>) => Array<string>): Object {
const newKeys = f(Object.keys(obj));
let ret = {};
for (let i = 0; i < newKeys.length; i++) {
const key = newKeys[i];
ret[key] = obj[key];
let hasWarned = false;
export default function reorderKeys() {
if (process.env.NODE_ENV === 'development') {
if (!hasWarned) {
hasWarned = true;
console.error(
'`reorderKeys` has been removed, since it is no longer needed for TransitionMotion\'s new styles array API.'
);
}
}
return ret;
}
/* @flow */
import presets from './presets';
import type {SpringConfig} from './Types';
import type {OpaqueConfig, SpringHelperConfig} from './Types';
export default function spring(
val: number,
config = presets.noWobble): SpringConfig {
return {val, config};
const defaultConfig = {
...presets.noWobble,
precision: 0.01,
};
export default function spring(val: number, config?: SpringHelperConfig): OpaqueConfig {
return {...defaultConfig, ...config, val};
}
/* @flow */
const errorMargin = 0.0001;
// stepper is used a lot. Saves allocation to return the same array wrapper.
// This is fine and danger-free against mutations because the callsite
// immediately destructures it and gets the numbers inside without passing the
// array reference around.
let reusedTuple: [number, number] = [];
export default function stepper(
frameRate: number,
secondPerFrame: number,
x: number,

@@ -10,3 +14,4 @@ v: number,

k: number,
b: number): [number, number] {
b: number,
precision: number): [number, number] {
// Spring stiffness, in kg / s^2

@@ -26,10 +31,14 @@

const newV = v + a * frameRate;
const newX = x + newV * frameRate;
const newV = v + a * secondPerFrame;
const newX = x + newV * secondPerFrame;
if (Math.abs(newV - v) < errorMargin && Math.abs(newX - x) < errorMargin) {
return [destX, 0];
if (Math.abs(newV) < precision && Math.abs(newX - destX) < precision) {
reusedTuple[0] = destX;
reusedTuple[1] = 0;
return reusedTuple;
}
return [newX, newV];
reusedTuple[0] = newX;
reusedTuple[1] = newV;
return reusedTuple;
}
/* @flow */
// turn {x: {val: 1, config: [1, 2]}, y: 2} generated by
// turn {x: {val: 1, stiffness: 1, damping: 2}, y: 2} generated by
// `{x: spring(1, [1, 2]), y: 2}` into {x: 1, y: 2}
import type {Style} from './Types';
import type {Style, PlainStyle} from './Types';
export default function stripStyle(style: Style): Object {
export default function stripStyle(style: Style): PlainStyle {
let ret = {};
for (let key in style) {
for (const key in style) {
if (!style.hasOwnProperty(key)) {
continue;
}
ret[key] = style[key] == null || style[key].val == null ? style[key] : style[key].val;
ret[key] = typeof style[key] === 'number' ? style[key] : style[key].val;
}
return ret;
}
/* @flow */
export type Style = Object;
export type Velocity = {
[key: string]: number,
// === basic reused types ===
// type of the second parameter of `spring(val, config)` all fields are optional
export type SpringHelperConfig = {
stiffness?: number,
damping?: number,
precision?: number,
};
export type SpringConfig = {
// the object returned by `spring(value, yourConfig)`. Used internally only!
export type OpaqueConfig = {
val: number,
config: [number, number],
stiffness: number,
damping: number,
precision: number,
};
export type TransitionStyles = {
[key: string]: Style,
// your typical style object given in props. Maps to a number or a spring config
export type Style = {[key: string]: number | OpaqueConfig};
// the interpolating style object, with the same keys as the above Style object,
// with the values mapped to numbers, naturally
export type PlainStyle = {[key: string]: number};
// internal velocity object. Similar to PlainStyle, but whose numbers represent
// speed. Might be exposed one day.
export type Velocity = {[key: string]: number};
// === Motion ===
export type MotionProps = {
defaultStyle?: PlainStyle,
style: Style,
children: (interpolatedStyle: PlainStyle) => ReactElement,
};
// === StaggeredMotion ===
export type StaggeredProps = {
defaultStyles?: Array<PlainStyle>,
styles: (previousInterpolatedStyles: ?Array<PlainStyle>) => Array<Style>,
children: (interpolatedStyles: Array<PlainStyle>) => ReactElement,
};
// === TransitionMotion ===
export type TransitionStyle = {
key: any, // unique ID to identify component across render animations
data?: any, // optional data you want to carry along the style, e.g. itemText
style: Style, // actual style you're passing
};
export type TransitionPlainStyle = {
key: any,
data?: any,
// same as TransitionStyle, passed as argument to style/children function
style: PlainStyle,
};
export type WillEnter = (styleThatEntered: TransitionStyle) => PlainStyle;
export type WillLeave = (styleThatLeft: TransitionStyle) => ?Style;
export type TransitionProps = {
defaultStyles?: Array<TransitionPlainStyle>,
styles: Array<TransitionStyle> | (previousInterpolatedStyles: ?Array<TransitionPlainStyle>) => Array<TransitionStyle>,
children: (interpolatedStyles: Array<TransitionPlainStyle>) => ReactElement,
willEnter?: WillEnter,
willLeave?: WillLeave,
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc