react-motion
Advanced tools
Comparing version 0.3.1 to 0.4.0
@@ -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 |
// 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", |
287
README.md
@@ -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.** | ||
### <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). | ||
--- | ||
### <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> | ||
``` | ||
### <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. | ||
--- | ||
### <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 @@ } | ||
### <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<PlainStyle>) => Array<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<PlainStyle> | ||
Optional. Similar to `Motion`'s `defaultStyle`, but an array of them. | ||
Similar but simpler logic for `willEnter`. | ||
##### - 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}]` | ||
#### `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. | ||
### <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<TransitionStyle> | (previousInterpolatedStyles: ?Array<TransitionPlainStyle>) => Array<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<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: ..., 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, | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
109822
2176
295
31
1