react-motion
Advanced tools
Comparing version 0.2.2 to 0.2.3
@@ -7,2 +7,9 @@ Legend: | ||
### 0.2.3 (July 29th 2015) | ||
- [I] React-native support! | ||
- [I] Allow returning `null` from children function. #101 | ||
- [I] `defaultValue` for specifying a... default value, upon mounting. | ||
- [I] `TransitionSpring`'s `willLeave` API got simplified and now asks for an object as a return value instead of `null`. `null` is still supported, but is deprecated and will be removed in the next version. See the new docs on it [here](https://github.com/chenglou/react-motion/blob/24d6a7284ef61268c0ead67fe43d7e40bf45d381/README.md#transitionspring-). | ||
- [I] Exposed a few tasteful default spring constants under the new exported `constants`. | ||
### 0.2.2 (July 24th 2015) | ||
@@ -9,0 +16,0 @@ - [F] Import some internal modules correctly for Ubuntu/Linux node (case-sensitive for them). |
'use strict'; | ||
exports.__esModule = true; | ||
exports['default'] = createAnimationLoop; | ||
exports['default'] = configAnimation; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
var _filter = require('./filter'); | ||
var _filter2 = _interopRequireDefault(_filter); | ||
var _performanceNow = require('performance-now'); | ||
@@ -20,115 +16,130 @@ | ||
function renderSubscriber(alpha, subscriber) { | ||
// subscriber.render: this.animationRender | ||
subscriber.render(alpha, subscriber.value, subscriber.prevValue); | ||
return subscriber.active; | ||
} | ||
function configAnimation() { | ||
var config = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var _config$timeStep = config.timeStep; | ||
var timeStep = _config$timeStep === undefined ? 1 / 60 * 1000 : _config$timeStep; | ||
var _config$timeScale = config.timeScale; | ||
var timeScale = _config$timeScale === undefined ? 1 : _config$timeScale; | ||
var _config$maxSteps = config.maxSteps; | ||
var maxSteps = _config$maxSteps === undefined ? 10 : _config$maxSteps; | ||
var _config$raf = config.raf; | ||
var raf = _config$raf === undefined ? _raf2['default'] : _config$raf; | ||
var _config$now = config.now; | ||
var now = _config$now === undefined ? _performanceNow2['default'] : _config$now; | ||
var prototype = { | ||
running: false, | ||
shouldStop: false, | ||
lastTime: 0, | ||
accumulatedTime: 0, | ||
var animRunning = []; | ||
var running = false; | ||
var prevTime = 0; | ||
var accumulatedTime = 0; | ||
// step: this.animationStep | ||
// render: this.animationRender | ||
// value: state | ||
subscribe: function subscribe(step, render, value) { | ||
var subscriber = { | ||
value: value, | ||
prevValue: value, | ||
step: step, | ||
render: render, | ||
active: true | ||
}; | ||
function loop() { | ||
var currentTime = now(); | ||
var frameTime = currentTime - prevTime; // delta | ||
this.subscribers.push(subscriber); | ||
prevTime = currentTime; | ||
accumulatedTime += frameTime * timeScale; | ||
return function unsubscribe() { | ||
subscriber.active = false; | ||
}; | ||
}, | ||
loop: function loop() { | ||
var currentTime = _performanceNow2['default'](); | ||
if (this.shouldStop) { | ||
this.running = this.shouldStop = false; | ||
return; | ||
if (accumulatedTime > timeStep * maxSteps) { | ||
accumulatedTime = 0; | ||
} | ||
var timeStep = this.timeStep; | ||
// delta | ||
var frameTime = currentTime - this.lastTime; | ||
var frameNumber = Math.ceil(accumulatedTime / timeStep); | ||
for (var i = 0; i < animRunning.length; i++) { | ||
var _animRunning$i = animRunning[i]; | ||
var active = _animRunning$i.active; | ||
var step = _animRunning$i.step; | ||
var prevPrevState = _animRunning$i.prevState; | ||
var prevNextState = _animRunning$i.nextState; | ||
this.lastTime = currentTime; | ||
this.accumulatedTime += frameTime * this.timeScale; | ||
if (!active) { | ||
continue; | ||
} | ||
if (this.accumulatedTime > timeStep * this.maxSteps) { | ||
this.accumulatedTime = 0; | ||
// Seems like because the TS sets destVals as enterVals for the first | ||
// tick, we might render that value twice. We render it once, currValue is | ||
// enterVal and destVal is enterVal. The next tick is faster than 16ms, | ||
// so accumulatedTime (which would be about -16ms from the previous tick) | ||
// is negative (-16ms + any number less than 16ms < 0). So we just render | ||
// part ways towards the nextState, but that's enterVal still. We render | ||
// say 75% between currValue (=== enterVal) and destValue (=== enterVal). | ||
// So we render the same value a second time. | ||
// The solution bellow is to recalculate the destination state even when | ||
// you're moving partially towards it. | ||
if (accumulatedTime <= 0) { | ||
animRunning[i].nextState = step(timeStep / 1000, prevPrevState); | ||
} else { | ||
for (var j = 0; j < frameNumber; j++) { | ||
animRunning[i].nextState = step(timeStep / 1000, prevNextState); | ||
animRunning[i].prevState = prevNextState; | ||
} | ||
} | ||
} | ||
while (this.accumulatedTime > 0) { | ||
this.subscribers.forEach(this.step); // animationLoop.step | ||
this.accumulatedTime -= timeStep; | ||
} | ||
accumulatedTime = accumulatedTime - frameNumber * timeStep; | ||
// Render and filter in one iteration. | ||
this.subscribers = _filter2['default'](this.subscribers, renderSubscriber, 1 + this.accumulatedTime / timeStep); | ||
var newAnimRunning = []; | ||
var alpha = 1 + accumulatedTime / timeStep; | ||
for (var i = 0; i < animRunning.length; i++) { | ||
var _animRunning$i2 = animRunning[i]; | ||
if (this.subscribers.length === 0) { | ||
this.shouldStop = true; | ||
// Might mutate animRunning........ | ||
var render = _animRunning$i2.render; | ||
var active = _animRunning$i2.active; | ||
var nextState = _animRunning$i2.nextState; | ||
var prevState = _animRunning$i2.prevState; | ||
render(alpha, nextState, prevState); | ||
if (active) { | ||
newAnimRunning.push(animRunning[i]); | ||
} | ||
} | ||
_raf2['default'](this.loop); | ||
}, | ||
animRunning = newAnimRunning; | ||
start: function start() { | ||
if (this.subscribers.length) { | ||
if (this.shouldStop) { | ||
this.shouldStop = false; | ||
} else if (!this.running) { | ||
this.running = true; | ||
this.lastTime = _performanceNow2['default'](); | ||
this.accumulatedTime = 0; | ||
_raf2['default'](this.loop); | ||
} | ||
if (animRunning.length === 0) { | ||
running = false; | ||
} else { | ||
raf(loop); | ||
} | ||
} | ||
}; | ||
function start() { | ||
if (!running) { | ||
running = true; | ||
prevTime = now(); | ||
accumulatedTime = 0; | ||
raf(loop); | ||
} | ||
} | ||
// stop() { | ||
// this.shouldStop = true; | ||
return function startAnimation(state, step, render) { | ||
for (var i = 0; i < animRunning.length; i++) { | ||
var val = animRunning[i]; | ||
if (val.step === step) { | ||
val.active = true; | ||
val.prevState = state; | ||
start(); | ||
return val.stop; | ||
} | ||
} | ||
// return this; | ||
// }, | ||
var newAnim = { | ||
step: step, | ||
render: render, | ||
prevState: state, | ||
nextState: state, | ||
active: true | ||
}; | ||
function createAnimationLoop(_ref) { | ||
var timeStep = _ref.timeStep; | ||
var timeScale = _ref.timeScale; | ||
var maxSteps = _ref.maxSteps; | ||
newAnim.stop = function () { | ||
return newAnim.active = false; | ||
}; | ||
animRunning.push(newAnim); | ||
var animationLoop = Object.create(prototype); | ||
start(); | ||
animationLoop.loop = animationLoop.loop.bind(animationLoop); | ||
animationLoop.subscribers = []; | ||
// timeStep is in milliseconds | ||
animationLoop.timeStep = timeStep * 1000; // seconds | ||
animationLoop.timeScale = timeScale; | ||
animationLoop.maxSteps = maxSteps; | ||
animationLoop.step = function (subscriber) { | ||
if (subscriber.active) { | ||
var value = subscriber.value; // value = this.state | ||
subscriber.prevValue = value; | ||
subscriber.value = subscriber.step(timeStep, value); // animationStep | ||
} | ||
return newAnim.stop; | ||
}; | ||
return animationLoop; | ||
} | ||
module.exports = exports['default']; |
@@ -7,3 +7,3 @@ // this function is allocation-less thanks to babel, which transforms the tail | ||
exports["default"] = mergeDiff; | ||
function mergeDiffArr2(_x, _x2, _x3, _x4, _x5, _x6, _x7) { | ||
function mergeDiffArr(_x, _x2, _x3, _x4, _x5, _x6, _x7) { | ||
var _again = true; | ||
@@ -106,3 +106,3 @@ | ||
// give you a medal | ||
mergeDiffArr2(Object.keys(a), Object.keys(b), b, 0, 0, onRemove, ret); | ||
mergeDiffArr(Object.keys(a), Object.keys(b), b, 0, 0, onRemove, ret); | ||
return ret; | ||
@@ -109,0 +109,0 @@ } |
@@ -15,2 +15,8 @@ 'use strict'; | ||
exports.TransitionSpring = _Spring.TransitionSpring; | ||
var _constants2 = require('./constants'); | ||
var _constants3 = _interopRequireDefault(_constants2); | ||
exports.constants = _constants3['default']; | ||
var utils = { | ||
@@ -17,0 +23,0 @@ reorderKeys: _reorderKeys2['default'] |
'use strict'; | ||
exports.__esModule = true; | ||
exports.updateCurrValue = updateCurrValue; | ||
exports.updateCurrVelocity = updateCurrVelocity; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
var _react = require('react'); | ||
var _react2 = _interopRequireDefault(_react); | ||
var _mapTree = require('./mapTree'); | ||
@@ -17,14 +11,10 @@ | ||
var _lodashIsplainobject = require('lodash.isplainobject'); | ||
var _noVelocity = require('./noVelocity'); | ||
var _lodashIsplainobject2 = _interopRequireDefault(_lodashIsplainobject); | ||
var _noVelocity2 = _interopRequireDefault(_noVelocity); | ||
var _stepper = require('./stepper'); | ||
var _compareTrees = require('./compareTrees'); | ||
var _stepper2 = _interopRequireDefault(_stepper); | ||
var _compareTrees2 = _interopRequireDefault(_compareTrees); | ||
var _noVelocity = require('./noVelocity'); | ||
var _noVelocity2 = _interopRequireDefault(_noVelocity); | ||
var _mergeDiff = require('./mergeDiff'); | ||
@@ -38,157 +28,108 @@ | ||
var animationLoop = _animationLoop2['default']({ | ||
// Fixed time step in seconds. | ||
timeStep: 1 / 60, | ||
// Slow-mo anyone? Give 0.1 a try. | ||
timeScale: 1, | ||
// Pause if we have more than this many steps worth of accumulated time. | ||
maxSteps: 10 | ||
}); | ||
var _zero = require('./zero'); | ||
function zero() { | ||
return 0; | ||
} | ||
var _zero2 = _interopRequireDefault(_zero); | ||
// TODO: refactor common logic with updateCurrValue and updateCurrVelocity | ||
function interpolateValue(alpha, nextValue, prevValue) { | ||
if (nextValue === null) { | ||
return null; | ||
} | ||
if (prevValue == null) { | ||
return nextValue; | ||
} | ||
if (typeof nextValue === 'number') { | ||
// https://github.com/chenglou/react-motion/pull/57#issuecomment-121924628 | ||
return nextValue * alpha + prevValue * (1 - alpha); | ||
} | ||
if (nextValue.val != null && nextValue.config && nextValue.config.length === 0) { | ||
return nextValue; | ||
} | ||
if (nextValue.val != null) { | ||
var ret = { | ||
val: interpolateValue(alpha, nextValue.val, prevValue.val) | ||
}; | ||
if (nextValue.config) { | ||
ret.config = nextValue.config; | ||
} | ||
return ret; | ||
} | ||
if (Array.isArray(nextValue)) { | ||
return nextValue.map(function (_, i) { | ||
return interpolateValue(alpha, nextValue[i], prevValue[i]); | ||
}); | ||
} | ||
if (_lodashIsplainobject2['default'](nextValue)) { | ||
return Object.keys(nextValue).reduce(function (ret, key) { | ||
ret[key] = interpolateValue(alpha, nextValue[key], prevValue[key]); | ||
return ret; | ||
}, {}); | ||
} | ||
return nextValue; | ||
var _updateTree = require('./updateTree'); | ||
var React = undefined; | ||
try { | ||
React = require('react-native'); | ||
} catch (e) { | ||
React = require('react'); | ||
} | ||
// TODO: refactor common logic with updateCurrVelocity | ||
var _React = React; | ||
var PropTypes = _React.PropTypes; | ||
function updateCurrValue(frameRate, currValue, currVelocity, endValue, k, b) { | ||
if (endValue === null) { | ||
return null; | ||
var startAnimation = _animationLoop2['default'](); | ||
function animationStep(shouldMerge, stopAnimation, getProps, timestep, state) { | ||
var currValue = state.currValue; | ||
var currVelocity = state.currVelocity; | ||
var _getProps = getProps(); | ||
var willEnter = _getProps.willEnter; | ||
var willLeave = _getProps.willLeave; | ||
var endValue = _getProps.endValue; | ||
if (typeof endValue === 'function') { | ||
endValue = endValue(currValue); | ||
} | ||
if (typeof endValue === 'number') { | ||
if (k == null || b == null) { | ||
return endValue; | ||
} | ||
// TODO: do something to stepper to make this not allocate (2 steppers?) | ||
return _stepper2['default'](frameRate, currValue, currVelocity, endValue, k, b)[0]; | ||
} | ||
if (endValue.val != null && endValue.config && endValue.config.length === 0) { | ||
return endValue; | ||
} | ||
if (endValue.val != null) { | ||
var _ref = endValue.config || [170, 26]; | ||
var _k = _ref[0]; | ||
var _b = _ref[1]; | ||
var mergedValue = endValue; // set mergedValue to endValue as the default | ||
var hasNewKey = false; | ||
var ret = { | ||
val: updateCurrValue(frameRate, currValue.val, currVelocity.val, endValue.val, _k, _b) | ||
}; | ||
if (endValue.config) { | ||
ret.config = endValue.config; | ||
} | ||
return ret; | ||
} | ||
if (Array.isArray(endValue)) { | ||
return endValue.map(function (_, i) { | ||
return updateCurrValue(frameRate, currValue[i], currVelocity[i], endValue[i], k, b); | ||
if (shouldMerge) { | ||
mergedValue = _mergeDiff2['default'](currValue, endValue, | ||
// TODO: stop allocating like crazy in this whole code path | ||
function (key) { | ||
var res = willLeave(key, currValue[key], endValue, currValue, currVelocity); | ||
if (res == null) { | ||
// For legacy reason. We won't allow returning null soon | ||
// TODO: remove, after next release | ||
return null; | ||
} | ||
if (_noVelocity2['default'](currVelocity[key]) && _compareTrees2['default'](currValue[key], res)) { | ||
return null; | ||
} | ||
return res; | ||
}); | ||
} | ||
if (_lodashIsplainobject2['default'](endValue)) { | ||
return Object.keys(endValue).reduce(function (ret, key) { | ||
ret[key] = updateCurrValue(frameRate, currValue[key], currVelocity[key], endValue[key], k, b); | ||
return ret; | ||
}, {}); | ||
} | ||
return endValue; | ||
} | ||
function updateCurrVelocity(frameRate, currValue, currVelocity, endValue, k, b) { | ||
if (endValue === null) { | ||
return null; | ||
Object.keys(mergedValue).filter(function (key) { | ||
return !currValue.hasOwnProperty(key); | ||
}).forEach(function (key) { | ||
hasNewKey = true; | ||
var enterValue = willEnter(key, mergedValue[key], endValue, currValue, currVelocity); | ||
currValue[key] = enterValue; | ||
mergedValue[key] = enterValue; | ||
currVelocity[key] = _mapTree2['default'](_zero2['default'], currValue[key]); | ||
}); | ||
} | ||
if (typeof endValue === 'number') { | ||
if (k == null || b == null) { | ||
return _mapTree2['default'](zero, currVelocity); | ||
} | ||
// TODO: do something to stepper to make this not allocate (2 steppers?) | ||
return _stepper2['default'](frameRate, currValue, currVelocity, endValue, k, b)[1]; | ||
} | ||
if (endValue.val != null && endValue.config && endValue.config.length === 0) { | ||
return _mapTree2['default'](zero, currVelocity); | ||
} | ||
if (endValue.val != null) { | ||
var _ref2 = endValue.config || [170, 26]; | ||
var _k = _ref2[0]; | ||
var _b = _ref2[1]; | ||
var newCurrValue = _updateTree.updateCurrValue(timestep, currValue, currVelocity, mergedValue); | ||
var newCurrVelocity = _updateTree.updateCurrVelocity(timestep, currValue, currVelocity, mergedValue); | ||
var ret = { | ||
val: updateCurrVelocity(frameRate, currValue.val, currVelocity.val, endValue.val, _k, _b) | ||
}; | ||
if (endValue.config) { | ||
ret.config = endValue.config; | ||
} | ||
return ret; | ||
if (!hasNewKey && _noVelocity2['default'](currVelocity) && _noVelocity2['default'](newCurrVelocity)) { | ||
// check explanation in `Spring.animationRender` | ||
stopAnimation(); // Nasty side effects.... | ||
} | ||
if (Array.isArray(endValue)) { | ||
return endValue.map(function (_, i) { | ||
return updateCurrVelocity(frameRate, currValue[i], currVelocity[i], endValue[i], k, b); | ||
}); | ||
} | ||
if (_lodashIsplainobject2['default'](endValue)) { | ||
return Object.keys(endValue).reduce(function (ret, key) { | ||
ret[key] = updateCurrVelocity(frameRate, currValue[key], currVelocity[key], endValue[key], k, b); | ||
return ret; | ||
}, {}); | ||
} | ||
return _mapTree2['default'](zero, currVelocity); | ||
return { | ||
currValue: newCurrValue, | ||
currVelocity: newCurrVelocity | ||
}; | ||
} | ||
var Spring = _react2['default'].createClass({ | ||
var Spring = React.createClass({ | ||
displayName: 'Spring', | ||
propTypes: { | ||
endValue: _react.PropTypes.oneOfType([_react.PropTypes.func, _react.PropTypes.object, _react.PropTypes.array]).isRequired, | ||
children: _react.PropTypes.func.isRequired | ||
defaultValue: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), | ||
endValue: PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.array]).isRequired, | ||
children: PropTypes.func.isRequired | ||
}, | ||
getInitialState: function getInitialState() { | ||
var endValue = this.props.endValue; | ||
var _props = this.props; | ||
var endValue = _props.endValue; | ||
var defaultValue = _props.defaultValue; | ||
if (typeof endValue === 'function') { | ||
// TODO: provide warning for failing to provide base case | ||
endValue = endValue(); | ||
var currValue = undefined; | ||
if (defaultValue == null) { | ||
if (typeof endValue === 'function') { | ||
// TODO: provide perf tip here when endValue argument count is 0 | ||
// (meaning you could have passed an obj) | ||
currValue = endValue(); | ||
} else { | ||
currValue = endValue; | ||
} | ||
} else { | ||
currValue = defaultValue; | ||
} | ||
return { | ||
currValue: endValue, | ||
currVelocity: _mapTree2['default'](zero, endValue) | ||
currValue: currValue, | ||
currVelocity: _mapTree2['default'](_zero2['default'], currValue) | ||
}; | ||
@@ -198,2 +139,9 @@ }, | ||
componentDidMount: function componentDidMount() { | ||
var _this = this; | ||
this.animationStep = animationStep.bind(null, false, function () { | ||
return _this.stopAnimation(); | ||
}, function () { | ||
return _this.props; | ||
}); | ||
this.startAnimating(); | ||
@@ -206,3 +154,3 @@ }, | ||
unsubscribeAnimation: null, | ||
stopAnimation: null, | ||
@@ -212,7 +160,6 @@ // used in animationRender | ||
animationStep: null, | ||
componentWillUnmount: function componentWillUnmount() { | ||
if (this.unsubscribeAnimation) { | ||
this.unsubscribeAnimation(); | ||
this.unsubscribeAnimation = null; | ||
} | ||
this.stopAnimation(); | ||
this.hasUnmounted = true; | ||
@@ -222,35 +169,6 @@ }, | ||
startAnimating: function startAnimating() { | ||
if (!this.unsubscribeAnimation) { | ||
// means we're not animating | ||
this.unsubscribeAnimation = animationLoop.subscribe(this.animationStep, this.animationRender, this.state); | ||
animationLoop.start(); | ||
} | ||
// Is smart enough to not start it twice | ||
this.stopAnimation = startAnimation(this.state, this.animationStep, this.animationRender); | ||
}, | ||
animationStep: function animationStep(timeStep, state) { | ||
var currValue = state.currValue; | ||
var currVelocity = state.currVelocity; | ||
var endValue = this.props.endValue; | ||
if (typeof endValue === 'function') { | ||
endValue = endValue(currValue); | ||
} | ||
var newCurrValue = updateCurrValue(timeStep, currValue, currVelocity, endValue); | ||
var newCurrVelocity = updateCurrVelocity(timeStep, currValue, currVelocity, endValue); | ||
if (_noVelocity2['default'](currVelocity) && _noVelocity2['default'](newCurrVelocity)) { | ||
// check explanation in `animationRender` | ||
if (!this.hasUnmounted) { | ||
this.unsubscribeAnimation(); | ||
this.unsubscribeAnimation = null; | ||
} | ||
} | ||
return { | ||
currValue: newCurrValue, | ||
currVelocity: newCurrVelocity | ||
}; | ||
}, | ||
animationRender: function animationRender(alpha, nextState, prevState) { | ||
@@ -263,3 +181,3 @@ // `this.hasUnmounted` might be true in the following condition: | ||
this.setState({ | ||
currValue: interpolateValue(alpha, nextState.currValue, prevState.currValue), | ||
currValue: _updateTree.interpolateValue(alpha, nextState.currValue, prevState.currValue), | ||
currVelocity: nextState.currVelocity | ||
@@ -271,5 +189,4 @@ }); | ||
render: function render() { | ||
var currValue = this.state.currValue; | ||
return _react2['default'].Children.only(this.props.children(currValue)); | ||
var renderedChildren = this.props.children(this.state.currValue); | ||
return renderedChildren && React.Children.only(renderedChildren); | ||
} | ||
@@ -279,10 +196,8 @@ }); | ||
exports.Spring = Spring; | ||
var TransitionSpring = _react2['default'].createClass({ | ||
var TransitionSpring = React.createClass({ | ||
displayName: 'TransitionSpring', | ||
propTypes: { | ||
endValue: _react.PropTypes.oneOfType([_react.PropTypes.func, _react.PropTypes.objectOf({ | ||
key: _react.PropTypes.any.isRequired | ||
})]). | ||
// coming soon | ||
defaultValue: PropTypes.objectOf(PropTypes.any), | ||
endValue: PropTypes.oneOfType([PropTypes.func, PropTypes.objectOf(PropTypes.any.isRequired)]). | ||
// PropTypes.arrayOf(PropTypes.shape({ | ||
@@ -293,12 +208,11 @@ // key: PropTypes.any.isRequired, | ||
isRequired, | ||
willLeave: _react.PropTypes.oneOfType([_react.PropTypes.func]), | ||
willLeave: PropTypes.oneOfType([PropTypes.func]), | ||
// PropTypes.object, | ||
// PropTypes.array, | ||
// TODO: numbers? strings? | ||
willEnter: _react.PropTypes.oneOfType([_react.PropTypes.func]), | ||
willEnter: PropTypes.oneOfType([PropTypes.func]), | ||
// PropTypes.object, | ||
// PropTypes.array, | ||
children: _react.PropTypes.func.isRequired | ||
children: PropTypes.func.isRequired | ||
}, | ||
@@ -318,10 +232,19 @@ | ||
getInitialState: function getInitialState() { | ||
var endValue = this.props.endValue; | ||
var _props2 = this.props; | ||
var endValue = _props2.endValue; | ||
var defaultValue = _props2.defaultValue; | ||
if (typeof endValue === 'function') { | ||
endValue = endValue(); | ||
var currValue = undefined; | ||
if (defaultValue == null) { | ||
if (typeof endValue === 'function') { | ||
currValue = endValue(); | ||
} else { | ||
currValue = endValue; | ||
} | ||
} else { | ||
currValue = defaultValue; | ||
} | ||
return { | ||
currValue: endValue, | ||
currVelocity: _mapTree2['default'](zero, endValue) | ||
currValue: currValue, | ||
currVelocity: _mapTree2['default'](_zero2['default'], currValue) | ||
}; | ||
@@ -331,2 +254,9 @@ }, | ||
componentDidMount: function componentDidMount() { | ||
var _this2 = this; | ||
this.animationStep = animationStep.bind(null, true, function () { | ||
return _this2.stopAnimation(); | ||
}, function () { | ||
return _this2.props; | ||
}); | ||
this.startAnimating(); | ||
@@ -339,3 +269,3 @@ }, | ||
unsubscribeAnimation: null, | ||
stopAnimation: null, | ||
@@ -345,64 +275,12 @@ // used in animationRender | ||
animationStep: null, | ||
componentWillUnmount: function componentWillUnmount() { | ||
if (this.unsubscribeAnimation) { | ||
this.unsubscribeAnimation(); | ||
this.unsubscribeAnimation = undefined; | ||
} | ||
this.stopAnimation(); | ||
}, | ||
startAnimating: function startAnimating() { | ||
if (!this.unsubscribeAnimation) { | ||
this.unsubscribeAnimation = animationLoop.subscribe(this.animationStep, this.animationRender, this.state); | ||
animationLoop.start(); | ||
} | ||
this.stopAnimation = startAnimation(this.state, this.animationStep, this.animationRender); | ||
}, | ||
animationStep: function animationStep(timeStep, state) { | ||
var currValue = state.currValue; | ||
var currVelocity = state.currVelocity; | ||
var endValue = this.props.endValue; | ||
var _props = this.props; | ||
var willEnter = _props.willEnter; | ||
var willLeave = _props.willLeave; | ||
if (typeof endValue === 'function') { | ||
endValue = endValue(currValue); | ||
} | ||
var mergedValue = undefined; | ||
// only other option is obj | ||
mergedValue = _mergeDiff2['default'](currValue, endValue, | ||
// TODO: stop allocating like crazy in this whole code path | ||
function (key) { | ||
return willLeave(key, currValue[key], endValue, currValue, currVelocity); | ||
}); | ||
var hasNewKey = false; | ||
Object.keys(mergedValue).filter(function (key) { | ||
return !currValue.hasOwnProperty(key); | ||
}).forEach(function (key) { | ||
hasNewKey = true; | ||
var enterValue = willEnter(key, mergedValue[key], endValue, currValue, currVelocity); | ||
currValue[key] = enterValue; | ||
mergedValue[key] = enterValue; | ||
currVelocity[key] = _mapTree2['default'](zero, currValue[key]); | ||
}); | ||
var newCurrValue = updateCurrValue(timeStep, currValue, currVelocity, mergedValue); | ||
var newCurrVelocity = updateCurrVelocity(timeStep, currValue, currVelocity, mergedValue); | ||
if (_noVelocity2['default'](currVelocity) && _noVelocity2['default'](newCurrVelocity) && !hasNewKey) { | ||
// check explanation in `Spring.animationRender` | ||
if (!this.hasUnmounted) { | ||
this.unsubscribeAnimation(); | ||
this.unsubscribeAnimation = undefined; | ||
} | ||
} | ||
return { | ||
currValue: newCurrValue, | ||
currVelocity: newCurrVelocity | ||
}; | ||
}, | ||
animationRender: function animationRender(alpha, nextState, prevState) { | ||
@@ -412,3 +290,3 @@ // See comment in Spring. | ||
this.setState({ | ||
currValue: interpolateValue(alpha, nextState.currValue, prevState.currValue), | ||
currValue: _updateTree.interpolateValue(alpha, nextState.currValue, prevState.currValue), | ||
currVelocity: nextState.currVelocity | ||
@@ -420,7 +298,6 @@ }); | ||
render: function render() { | ||
var currValue = this.state.currValue; | ||
return _react2['default'].Children.only(this.props.children(currValue)); | ||
var renderedChildren = this.props.children(this.state.currValue); | ||
return renderedChildren && React.Children.only(renderedChildren); | ||
} | ||
}); | ||
exports.TransitionSpring = TransitionSpring; |
{ | ||
"name": "react-motion", | ||
"version": "0.2.2", | ||
"version": "0.2.3", | ||
"description": "A spring that solves your animation problems.", | ||
@@ -18,3 +18,3 @@ "main": "lib/react-motion.js", | ||
"eslint-plugin-react": "^2.7.0", | ||
"inject-loader": "^2.0.0", | ||
"inject-loader": "plasticine/inject-loader#master", | ||
"isparta-loader": "^0.2.0", | ||
@@ -21,0 +21,0 @@ "karma": "^0.12.37", |
@@ -13,3 +13,3 @@ # React-Motion | ||
Animate a counter to `10`, from whatever value it currently is. For more advanced usage, see below. | ||
Animate a counter to `10`, from whatever value it currently is. For more advanced usage, see [below](#constants). | ||
@@ -26,4 +26,6 @@ Npm: | ||
[Check](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo0/index.html) [Out](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo1/index.html) [The](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo2/index.html) [Cool](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo3/index.html) [Demos](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo4/index.html). | ||
Or build it yourself from the repo: `git clone https://github.com/chenglou/react-motion.git && npm i && npm run prerelease` | ||
[Check](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo0/index.html) [Out](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo1/index.html) [The](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo2/index.html) [Cool](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo3/index.html) [Demos](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo4/index.html) [Here](https://cdn.rawgit.com/chenglou/react-motion/2a5f89bee700c6bbb0560eee0c8f9ad66d111bdd/demos/demo5/index.html). | ||
## What does this library try to solve? | ||
@@ -55,3 +57,3 @@ | ||
<button onMouseDown={this.handleMouseDown}>Toggle</button> | ||
<Spring endValue={{val: this.state.open ? 400 : 0}}> | ||
<Spring defaultValue={{val: 0}} endValue={{val: this.state.open ? 400 : 0}}> | ||
{interpolated => | ||
@@ -69,10 +71,17 @@ <div className="demo0-block" style={{ | ||
The library exports a `Spring`, a `TransitionSpring` and `utils`. | ||
The library exports a `Spring`, a `TransitionSpring`, `constants` `utils`. | ||
### <Spring /> | ||
Exposes a single prop, `endValue`, which takes either an object, an array or a function that returns an object or an array. | ||
Type: `endValue: object | array | object -> (object | array)`. | ||
Exposes the props `defaultValue` (object or array) and `endValue` (object, array or a function that returns an object or an array). | ||
`endValue` can be of an arbitrary shape (**but must stay the same shape from one render to the next**). There are however 2 reserved keys: `val` and `config`. Say your initial data structure looks so: | ||
Types: | ||
- `defaultValue: object | array`. | ||
- `endValue: object | array | object -> (object | array)`. | ||
Both values can be of an arbitrary shape (**but must have the same shape. `endValue` must stay the same shape from one render to the next**). | ||
`defaultValue` is used as the first value upon mounting. Usually your `endValue` would differ from it, as to give the mounting animation effect. | ||
`endValue` is the value you want to reach. There are 2 reserved keys for it: `val` and `config`. Say your initial data structure looks so: | ||
```js | ||
@@ -113,3 +122,3 @@ {size: 10, top: 20} | ||
A stiffness of `120` and damping of `17` gives the spring a slight bounce effect. The default configuration, if you don't pass `config` alongside `val`, is `[170, 26]`. | ||
A stiffness of `120` and damping of `17` gives the spring a slight bounce effect. The library exports a `constants` object that contains the commonly used stiffness and damping. See the `constants` section below. | ||
@@ -170,16 +179,10 @@ You can nest `val` wrappers; the innermost takes priority: | ||
Sometime, you want to rely on the currently interpolated value to calculate `endValue`. E.g. (demo 1) a chat head's final position is the current position of the leading chat head. `endValue` can also accept a function `(currentPositions) => yourEndValue`, where `currentPositions` is the same data structure you'd receive from the children callback. | ||
Sometime, you want to rely on the currently interpolated value to calculate `endValue`. E.g. (demo 1) a chat head's final position is the current position of the leading chat head. `endValue` can also accept a function `(prevValue) => yourEndValue`, where `prevValue` is the data you returned from the previous tick of `getEndValue`. | ||
```jsx | ||
// ...Somewhere in your React class | ||
getEndValues: function(currentPositions) { | ||
// currentPositions of `null` means it's the first render for Spring. | ||
if (currentPositions == null) { | ||
return {val: utils.range(6).map(() => [0, 0])}; | ||
} | ||
// This is really the previous tick of currentPositions. In practice, it | ||
// doesn't make much difference. | ||
let endValue = currentPositions.val.map((_, i) => { | ||
getEndValue: function(prevValue) { | ||
let endValue = prevValue.val.map((_, i) => { | ||
// First one follows the mouse | ||
return i === 0 ? this.state.mousePosition : currentPositions.val[i - 1]; | ||
return i === 0 ? this.state.mousePosition : prevValue.val[i - 1]; | ||
}); | ||
@@ -190,2 +193,3 @@ // Have fun adjusting config to make the chat heads bounce a little more! | ||
// in render: <Spring endValue={this.getEndValue}></Spring> | ||
``` | ||
@@ -196,2 +200,4 @@ | ||
`defaultValue`: see `endValue` just below. | ||
`endValue`: now constrained to an object (or a callback `currentValue -> object`) of the shape `{key => yourStuff}` (the data is constrained to this shape, but that doesn't mean the way you use your interpolated value has to be). When `endValue` differs from the current interpolated value by an added/removed key: | ||
@@ -201,3 +207,3 @@ | ||
`willLeave`: a callback that's called **many** times and is passed `(keyThatLeaves, correspondingValueOfKeyThatJustLeft, endValueYouJustSpecified, currentInterpolatedValue, currentSpeed)`. Return an object/array configuration (which will serve as the new `endValue[keyThatLeaves]` and merged into `endValue`) to indicate you still want to keep the item around. Otherwise, return `null`. | ||
`willLeave`: a callback that's called **many** times and is passed `(keyThatLeaves, correspondingValueOfKeyThatJustLeft, endValueYouJustSpecified, currentInterpolatedValue, currentSpeed)`. Return an object/array configuration (which will serve as the new `endValue[keyThatLeaves]` and merged into `endValue`). When that value is reached through the spring interpolation, the key will be actually unmounted. | ||
@@ -260,3 +266,3 @@ #### Sample Usage | ||
<TransitionSpring | ||
endValue={this.getEndValue} | ||
endValue={this.getEndValue()} | ||
willEnter={this.willEnter} | ||
@@ -285,2 +291,5 @@ willLeave={this.willLeave}> | ||
### `constants` | ||
Some tasteful, commonly used spring constants you can plug into your `endValue` like so: `{val: 10, config: constants.wobbly}`. [See here](https://github.com/chenglou/react-motion/blob/372446fc8679dcdbf4de2e983d53709ab40ce88c/src/constants.js). | ||
### Little Extras | ||
@@ -296,1 +305,28 @@ _(You might not need this until later on.)_ | ||
`newKeysFunction` 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 missing). `reorderKeys` will then return a new object similar to `object`, but with the keys in the order `newKeysFunction` dictated. | ||
## FAQ | ||
- How do I do staggering/chained animation where items animate in one after another? | ||
In most cases, what you want to express here is a relationship between animations, e.g. item 2 appears after item 1. Staggering/chained animation have hard-coded values and go against the spirit of a physics system. Check out [demo 1](https://cdn.rawgit.com/chenglou/react-motion/3b5be548cd08630a836562a053576ff91f94b93f/demo1/index.html); each ball follows the one in front of it, creating a natural staggering animation. The code in `endValue` looks roughly so: | ||
```jsx | ||
<Spring endValue={prevValue => { | ||
const endValue = prevValue.val.map( | ||
(_, i) => i === 0 ? someMousePosition : prevValue.val[i - 1] | ||
); | ||
return {val: endValue}; | ||
}}> | ||
... | ||
``` | ||
First ball's destination is the mouse position. The subsequent ones' destination is the current position of the ball in front of them (technically, the previous tick's position; Doesn't matter much). The values depend on each other. No hard-coded duration/timeout here! | ||
- My `ref` doesn't work in the children function. | ||
React string refs won't work: | ||
```jsx | ||
<Spring endValue={...}> | ||
{currentValue => <div ref="stuff" />} | ||
</Spring> | ||
``` | ||
This is how React works. Here's the [callback ref solution](https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute). |
@@ -34,3 +34,4 @@ var webpack = require('webpack'); | ||
amd: 'react' | ||
} | ||
}, | ||
'react-native': true, | ||
}, | ||
@@ -37,0 +38,0 @@ }; |
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
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
46595
22
851
321