return this.payload.map(function (v) {
_proto.interpolate = function interpolate(range, output) {
return new AnimatedInterpolation(this, range, output);
return AnimatedValueArray;
this.interpolations = {};
this.localQueue = void 0;
this.getValues = function () {
return _this.interpolations;
* This function filters input props and creates an array of tasks which are executed in .start()
* Each task is allowed to carry a delay, which means it can execute asnychroneously */
var _proto = Controller.prototype;
_proto.update = function update$$1(args) {
if (!args) return this; // Extract delay and the to-prop from props
var _ref = interpolateTo(args),
delay = _ref$delay === void 0 ? 0 : _ref$delay,
props = _objectWithoutPropertiesLoose(_ref, ["delay", "to"]);
if (is.arr(to) || is.fun(to)) {
// If config is either a function or an array queue it up as is
this.queue.push(_extends({}, props, {
// Otherwise go through each key since it could be delayed individually
Object.entries(to).forEach(function (_ref2) {
// Fetch delay and create an entry, consisting of the to-props, the delay, and basic props
to: (_to = {}, _to[k] = v, _to),
delay: callProp(delay, k)
var previous = ops[entry.delay] && ops[entry.delay].to;
ops[entry.delay] = _extends({}, ops[entry.delay], entry, {
to: _extends({}, previous, entry.to)
this.queue = Object.values(ops);
} // Sort queue, so that async calls go last
this.queue = this.queue.sort(function (a, b) {
return a.delay - b.delay;
}); // Diff the reduced props immediately (they'll contain the from-prop and some config)
* This function either executes a queue, if present, or starts the frameloop, which animates */
_proto.start = function start$$1(onEnd) {
// If a queue is present we must excecute it
this.idle = false; // Updates can interrupt trailing queues, in that case we just merge values
this.localQueue.forEach(function (_ref3) {
var _ref3$from = _ref3.from,
from = _ref3$from === void 0 ? {} : _ref3$from,
to = _ref3$to === void 0 ? {} : _ref3$to;
if (is.obj(from)) _this2.merged = _extends({}, from, _this2.merged);
if (is.obj(to)) _this2.merged = _extends({}, _this2.merged, to);
} // The guid helps us tracking frames, a new queue over an old one means an override
// We discard async calls in that caseÍ
var local = this.local = ++this.guid;
var queue = this.localQueue = this.queue;
this.queue = []; // Go through each entry and execute it
queue.forEach(function (_ref4, index) {
props = _objectWithoutPropertiesLoose(_ref4, ["delay"]);
var cb = function cb(finished) {
if (index === queue.length - 1 && local === _this2.guid && finished) {
if (_this2.props.onRest) _this2.props.onRest(_this2.merged);
}; // Entries can be delayed, ansyc or immediate
var async = is.arr(props.to) || is.fun(props.to);
if (local === _this2.guid) {
if (async) _this2.runAsync(props, cb);else _this2.diff(props).start(cb);
} else if (async) _this2.runAsync(props, cb);else _this2.diff(props).start(cb);
} // Otherwise we kick of the frameloop
if (is.fun(onEnd)) this.listeners.push(onEnd);
if (this.props.onStart) this.props.onStart();
_proto.stop = function stop$$1(finished) {
this.listeners.forEach(function (onEnd) {
/** Pause sets onEnd listeners free, but also removes the controller from the frameloop */
_proto.pause = function pause(finished) {
if (finished) stop(this);
_proto.runAsync = function runAsync(_ref5, onEnd) {
props = _objectWithoutPropertiesLoose(_ref5, ["delay"]);
var local = this.local; // If "to" is either a function or an array it will be processed async, therefor "to" should be empty right now
// If the view relies on certain values "from" has to be present
var queue = Promise.resolve(undefined);
var _loop = function _loop(i) {
var fresh = _extends({}, props, interpolateTo(props.to[index]));
if (is.arr(fresh.config)) fresh.config = fresh.config[index];
queue = queue.then(function () {
if (local === _this3.guid) return new Promise(function (r) {
return _this3.diff(fresh).start(r);
for (var i = 0; i < props.to.length; i++) {
} else if (is.fun(props.to)) {
queue = queue.then(function () {
return props.to( // next(props)
var fresh = _extends({}, props, interpolateTo(p));
if (is.arr(fresh.config)) fresh.config = fresh.config[index];
if (local === _this3.guid) return last = new Promise(function (r) {
return _this3.diff(fresh).start(r);
if (finished === void 0) {
return _this3.stop(finished);
_proto.diff = function diff(props) {
this.props = _extends({}, this.props, props);
var _this$props = this.props,
_this$props$from = _this$props.from,
from = _this$props$from === void 0 ? {} : _this$props$from,
_this$props$to = _this$props.to,
to = _this$props$to === void 0 ? {} : _this$props$to,
_this$props$config = _this$props.config,
config = _this$props$config === void 0 ? {} : _this$props$config,
reverse = _this$props.reverse,
attach = _this$props.attach,
reset = _this$props.reset,
immediate = _this$props.immediate; // Reverse values when requested
} // This will collect all props that were ever set, reset merged props when necessary
this.merged = _extends({}, from, this.merged, to);
this.hasChanged = false; // Attachment handling, trailed springs can "attach" themselves to a previous spring
var target = attach && attach(this); // Reduces input { name: value } pairs into animated values
this.animations = Object.entries(this.merged).reduce(function (acc, _ref7) {
// Issue cached entries, except on reset
var entry = acc[name] || {}; // Figure out what the value is supposed to be
var isNumber = is.num(value);
var isString = is.str(value) && !value.startsWith('#') && !/\d/.test(value) && !colorNames[value];
var isArray = is.arr(value);
var isInterpolation = !isNumber && !isArray && !isString;
var fromValue = !is.und(from[name]) ? from[name] : value;
var toValue = isNumber || isArray ? value : isString ? value : 1;
var toConfig = callProp(config, name);
if (target) toValue = target.animations[name].parent;
var parent = entry.parent,
interpolation$$1 = entry.interpolation,
toValues = toArray(target ? toValue.getPayload() : toValue),
if (isInterpolation) newValue = interpolation({
var currentValue = interpolation$$1 && interpolation$$1.getValue(); // Change detection flags
var isFirst = is.und(parent);
var isActive = !isFirst && entry.animatedValues.some(function (v) {
var currentValueDiffersFromGoal = !is.equ(newValue, currentValue);
var hasNewGoal = !is.equ(newValue, entry.previous);
var hasNewConfig = !is.equ(toConfig, entry.config); // Change animation props when props indicate a new goal (new value differs from previous one)
// and current values differ from it. Config changes trigger a new update as well (though probably shouldn't?)
if (reset || hasNewGoal && currentValueDiffersFromGoal || hasNewConfig) {
// Convert regular values into animated values, ALWAYS re-use if possible
if (isNumber || isString) parent = interpolation$$1 = entry.parent || new AnimatedValue(fromValue);else if (isArray) parent = interpolation$$1 = entry.parent || new AnimatedValueArray(fromValue);else if (isInterpolation) {
var prev = entry.interpolation && entry.interpolation.calc(entry.parent.value);
prev = prev !== void 0 && !reset ? prev : fromValue;
parent.setValue(0, false);
} else parent = new AnimatedValue(0);
if (entry.interpolation) {
interpolation$$1 = entry.interpolation;
entry.interpolation.updateConfig(range);
} else interpolation$$1 = parent.interpolate(range);
toValues = toArray(target ? toValue.getPayload() : toValue);
animatedValues = toArray(parent.getPayload());
if (reset && !isInterpolation) parent.setValue(fromValue, false);
_this4.hasChanged = true; // Reset animated values
animatedValues.forEach(function (value) {
value.startPosition = value.value;
value.lastPosition = value.value;
value.lastVelocity = isActive ? value.lastVelocity : undefined;
value.lastTime = isActive ? value.lastTime : undefined;
value.animatedStyles.clear();
}); // Set immediate values
if (callProp(immediate, name)) {
parent.setValue(isInterpolation ? toValue : value, false);
return _extends({}, acc, (_extends2 = {}, _extends2[name] = _extends({}, entry, {
interpolation: interpolation$$1,
animatedValues: animatedValues,
fromValues: toArray(parent.getValue()),
immediate: callProp(immediate, name),
initialVelocity: withDefault(toConfig.velocity, 0),
clamp: withDefault(toConfig.clamp, false),
precision: withDefault(toConfig.precision, 0.01),
tension: withDefault(toConfig.tension, 170),
friction: withDefault(toConfig.friction, 26),
mass: withDefault(toConfig.mass, 1),
duration: toConfig.duration,
easing: withDefault(toConfig.easing, function (t) {
if (!currentValueDiffersFromGoal) {
// So ... the current target value (newValue) appears to be different from the previous value,
// which normally constitutes an update, but the actual value (currentValue) matches the target!
// In order to resolve this without causing an animation update we silently flag the animation as done,
// which it technically is. Interpolations also needs a config update with their target set to 1.
parent.setValue(1, false);
interpolation$$1.updateConfig({
output: [newValue, newValue]
_this4.hasChanged = true;
return _extends({}, acc, (_extends3 = {}, _extends3[name] = _extends({}, acc[name], {
// Make animations available to frameloop
this.configs = Object.values(this.animations);
this.interpolations = {};
for (var key in this.animations) {
this.interpolations[key] = this.animations[key].interpolation;
this.values[key] = this.animations[key].interpolation.getValue();
_proto.destroy = function destroy() {
this.interpolations = {};
* const props = useSprings(number, [{ ... }, { ... }, ...])
* const [props, set] = useSprings(number, (i, controller) => ({ ... }))
var useSprings = function useSprings(length, props) {
var mounted = React.useRef(false);
var ctrl = React.useRef();
var isFn = is.fun(props); // The controller maintains the animation values, starts and stops animations
var _useMemo = React.useMemo(function () {
// Remove old controllers
ctrl.current.map(function (c) {
ctrl.current = undefined;
return [new Array(length).fill().map(function (_, i) {
var ctrl = new Controller();
var newProps = isFn ? callProp(props, i, ctrl) : props[i];
if (i === 0) ref = newProps.ref;
controllers = _useMemo[0],
ctrl.current = controllers; // The hooks reference api gets defined here ...
var api = React.useImperativeHandle(ref, function () {
start: function start() {
return Promise.all(ctrl.current.map(function (c) {
return new Promise(function (r) {
stop: function stop(finished) {
return ctrl.current.forEach(function (c) {
}); // This function updates the controllers
var updateCtrl = React.useMemo(function () {
return function (updateProps) {
return ctrl.current.map(function (c, i) {
c.update(isFn ? callProp(updateProps, i, c) : updateProps[i]);
}, [length]); // Update controller if props aren't functional
React.useEffect(function () {
if (!isFn) updateCtrl(props);
} else if (!ref) ctrl.current.forEach(function (c) {
}); // Update mounted flag and destroy controller on unmount
React.useEffect(function () {
return mounted.current = true, function () {
return ctrl.current.forEach(function (c) {
}, []); // Return animated props, or, anim-props + the update-setter above
var propValues = ctrl.current.map(function (c) {
return isFn ? [propValues, updateCtrl, function (finished) {
return ctrl.current.forEach(function (c) {