react-native-mock-tmp-build
Version:
A fully mocked and test-friendly version of react native
1,183 lines (1,041 loc) • 29.5 kB
JavaScript
import invariant from 'invariant';
import Interpolation from './Interpolation';
import Easing from './Easing';
import InteractionManager from '../InteractionManager';
import SpringConfig from './SpringConfig';
import requestAnimationFrame from 'raf';
import flattenStyle from '../../propTypes/flattenStyle';
class Animated {
__attach() {}
__detach() {}
__getValue() {}
__getAnimatedValue() { return this.__getValue(); }
__addChild(child) {}
__removeChild(child) {}
__getChildren() { return []; }
}
class Animation {
start(fromValue, onUpdate, onEnd, previousAnimation) {}
stop() {}
__debouncedOnEnd(result) {
const onEnd = this.__onEnd;
this.__onEnd = null;
if (onEnd) {
onEnd(result);
}
}
}
class AnimatedWithChildren extends Animated {
constructor() {
super();
this._children = [];
}
__addChild(child) {
if (this._children.length === 0) {
this.__attach();
}
this._children.push(child);
}
__removeChild(child) {
const index = this._children.indexOf(child);
if (index === -1) {
console.warn(
'Trying to remove a child that doesn\'t exist'
);
return;
}
this._children.splice(index, 1);
if (this._children.length === 0) {
this.__detach();
}
}
__getChildren() {
return this._children;
}
}
/**
* Animated works by building a directed acyclic graph of dependencies
* transparently when you render your Animated components.
*
* new Animated.Value(0)
* .interpolate() .interpolate() new Animated.Value(1)
* opacity translateY scale
* style transform
* View#234 style
* View#123
*
* A) Top Down phase
* When an Animated.Value is updated, we recursively go down through this
* graph in order to find leaf nodes: the views that we flag as needing
* an update.
*
* B) Bottom Up phase
* When a view is flagged as needing an update, we recursively go back up
* in order to build the new value that it needs. The reason why we need
* this two-phases process is to deal with composite props such as
* transform which can receive values from multiple parents.
*/
function _flush(rootNode) {
const animatedStyles = new Set();
function findAnimatedStyles(node) {
if (typeof node.update === 'function') {
animatedStyles.add(node);
} else {
node.__getChildren().forEach(findAnimatedStyles);
}
}
findAnimatedStyles(rootNode);
animatedStyles.forEach(animatedStyle => animatedStyle.update());
}
const easeInOut = Easing.inOut(Easing.ease);
class TimingAnimation extends Animation {
constructor(config) {
super();
this._toValue = config.toValue;
this._easing = config.easing || easeInOut;
this._duration = config.duration !== undefined ? config.duration : 500;
this._delay = config.delay || 0;
this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true;
}
start(fromValue, onUpdate, onEnd) {
this.__active = true;
this._fromValue = fromValue;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
const start = () => {
if (this._duration === 0) {
this._onUpdate(this._toValue);
this.__debouncedOnEnd({ finished: true });
} else {
this._startTime = Date.now();
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
};
if (this._delay) {
this._timeout = setTimeout(start, this._delay);
} else {
start();
}
}
onUpdate() {
const now = Date.now();
if (now >= this._startTime + this._duration) {
if (this._duration === 0) {
this._onUpdate(this._toValue);
} else {
this._onUpdate(
this._fromValue + this._easing(1) * (this._toValue - this._fromValue)
);
}
this.__debouncedOnEnd({ finished: true });
return;
}
this._onUpdate(
this._fromValue +
this._easing((now - this._startTime) / this._duration) *
(this._toValue - this._fromValue)
);
if (this.__active) {
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
stop() {
this.__active = false;
clearTimeout(this._timeout);
if (global && global.cancelAnimationFrame) {
global.cancelAnimationFrame(this._animationFrame);
}
this.__debouncedOnEnd({ finished: false });
}
}
class DecayAnimation extends Animation {
constructor(config) {
super();
this._deceleration = config.deceleration || 0.998;
this._velocity = config.velocity;
this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true;
}
start(fromValue, onUpdate, onEnd) {
this.__active = true;
this._lastValue = fromValue;
this._fromValue = fromValue;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
this._startTime = Date.now();
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
onUpdate() {
const now = Date.now();
const value = this._fromValue +
(this._velocity / (1 - this._deceleration)) *
(1 - Math.exp(-(1 - this._deceleration) * (now - this._startTime)));
this._onUpdate(value);
if (Math.abs(this._lastValue - value) < 0.1) {
this.__debouncedOnEnd({ finished: true });
return;
}
this._lastValue = value;
if (this.__active) {
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
stop() {
this.__active = false;
if (global && global.cancelAnimationFrame) {
global.cancelAnimationFrame(this._animationFrame);
}
this.__debouncedOnEnd({ finished: false });
}
}
function withDefault(value, defaultValue) {
if (value === undefined || value === null) {
return defaultValue;
}
return value;
}
class SpringAnimation extends Animation {
constructor(config) {
super();
this._overshootClamping = withDefault(config.overshootClamping, false);
this._restDisplacementThreshold = withDefault(config.restDisplacementThreshold, 0.001);
this._restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001);
this._initialVelocity = config.velocity;
this._lastVelocity = withDefault(config.velocity, 0);
this._toValue = config.toValue;
this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true;
let springConfig;
if (config.bounciness !== undefined || config.speed !== undefined) {
invariant(
config.tension === undefined && config.friction === undefined,
'You can only define bounciness/speed or tension/friction but not both'
);
springConfig = SpringConfig.fromBouncinessAndSpeed(
withDefault(config.bounciness, 8),
withDefault(config.speed, 12),
);
} else {
springConfig = SpringConfig.fromOrigamiTensionAndFriction(
withDefault(config.tension, 40),
withDefault(config.friction, 7),
);
}
this._tension = springConfig.tension;
this._friction = springConfig.friction;
}
start(fromValue, onUpdate, onEnd, previousAnimation) {
this.__active = true;
this._startPosition = fromValue;
this._lastPosition = this._startPosition;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
this._lastTime = Date.now();
if (previousAnimation instanceof SpringAnimation) {
const internalState = previousAnimation.getInternalState();
this._lastPosition = internalState.lastPosition;
this._lastVelocity = internalState.lastVelocity;
this._lastTime = internalState.lastTime;
}
if (this._initialVelocity !== undefined && this._initialVelocity !== null) {
this._lastVelocity = this._initialVelocity;
}
this.onUpdate();
}
getInternalState() {
return {
lastPosition: this._lastPosition,
lastVelocity: this._lastVelocity,
lastTime: this._lastTime,
};
}
onUpdate() {
let position = this._lastPosition;
let velocity = this._lastVelocity;
let tempPosition = this._lastPosition;
let tempVelocity = this._lastVelocity;
// If for some reason we lost a lot of frames (e.g. process large payload or
// stopped in the debugger), we only advance by 4 frames worth of
// computation and will continue on the next frame. It's better to have it
// running at faster speed than jumping to the end.
const MAX_STEPS = 64;
let now = Date.now();
if (now > this._lastTime + MAX_STEPS) {
now = this._lastTime + MAX_STEPS;
}
// We are using a fixed time step and a maximum number of iterations.
// The following post provides a lot of thoughts into how to build this
// loop: http://gafferongames.com/game-physics/fix-your-timestep/
const TIMESTEP_MSEC = 1;
const numSteps = Math.floor((now - this._lastTime) / TIMESTEP_MSEC);
for (let i = 0; i < numSteps; ++i) {
// Velocity is based on seconds instead of milliseconds
const step = TIMESTEP_MSEC / 1000;
// This is using RK4. A good blog post to understand how it works:
// http://gafferongames.com/game-physics/integration-basics/
const aVelocity = velocity;
const aAcceleration =
this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
tempPosition = position + aVelocity * step / 2;
tempVelocity = velocity + aAcceleration * step / 2;
const bVelocity = tempVelocity;
const bAcceleration =
this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
tempPosition = position + bVelocity * step / 2;
tempVelocity = velocity + bAcceleration * step / 2;
const cVelocity = tempVelocity;
const cAcceleration =
this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
tempPosition = position + cVelocity * step / 2;
tempVelocity = velocity + cAcceleration * step / 2;
const dVelocity = tempVelocity;
const dAcceleration =
this._tension * (this._toValue - tempPosition) - this._friction * tempVelocity;
tempPosition = position + cVelocity * step / 2;
tempVelocity = velocity + cAcceleration * step / 2;
const dxdt = (aVelocity + 2 * (bVelocity + cVelocity) + dVelocity) / 6;
const dvdt = (aAcceleration + 2 * (bAcceleration + cAcceleration) + dAcceleration) / 6;
position += dxdt * step;
velocity += dvdt * step;
}
this._lastTime = now;
this._lastPosition = position;
this._lastVelocity = velocity;
this._onUpdate(position);
if (!this.__active) { // a listener might have stopped us in _onUpdate
return;
}
// Conditions for stopping the spring animation
let isOvershooting = false;
if (this._overshootClamping && this._tension !== 0) {
if (this._startPosition < this._toValue) {
isOvershooting = position > this._toValue;
} else {
isOvershooting = position < this._toValue;
}
}
const isVelocity = Math.abs(velocity) <= this._restSpeedThreshold;
let isDisplacement = true;
if (this._tension !== 0) {
isDisplacement = Math.abs(this._toValue - position) <= this._restDisplacementThreshold;
}
if (isOvershooting || (isVelocity && isDisplacement)) {
if (this._tension !== 0) {
// Ensure that we end up with a round value
this._onUpdate(this._toValue);
}
this.__debouncedOnEnd({ finished: true });
return;
}
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
stop() {
this.__active = false;
window.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({ finished: false });
}
}
let _uniqueId = 1;
class AnimatedInterpolation extends AnimatedWithChildren {
constructor(parent, interpolation) {
super();
this._parent = parent;
this._interpolation = interpolation;
}
__getValue() {
const parentValue = this._parent.__getValue();
invariant(
typeof parentValue === 'number',
'Cannot interpolate an input which is not a number.'
);
return this._interpolation(parentValue);
}
interpolate(config) {
return new AnimatedInterpolation(this, Interpolation.create(config));
}
__attach() {
this._parent.__addChild(this);
}
__detach() {
this._parent.__removeChild(this);
}
}
class AnimatedValue extends AnimatedWithChildren {
constructor(value) {
super();
this._value = value;
this._offset = 0;
this._animation = null;
this._listeners = {};
}
__detach() {
this.stopAnimation();
}
__getValue() {
return this._value + this._offset;
}
/**
* Directly set the value. This will stop any animations running on the value
* and update all the bound properties.
*/
setValue(value) {
if (this._animation) {
this._animation.stop();
this._animation = null;
}
this._updateValue(value);
}
/**
* Sets an offset that is applied on top of whatever value is set, whether via
* `setValue`, an animation, or `Animated.event`. Useful for compensating
* things like the start of a pan gesture.
*/
setOffset(offset) {
this._offset = offset;
}
/**
* Merges the offset value into the base value and resets the offset to zero.
* The final output of the value is unchanged.
*/
flattenOffset() {
this._value += this._offset;
this._offset = 0;
}
/**
* Adds an asynchronous listener to the value so you can observe updates from
* animations. This is useful because there is no way to
* synchronously read the value because it might be driven natively.
*/
addListener(callback) {
const id = String(_uniqueId++);
this._listeners[id] = callback;
return id;
}
removeListener(id) {
delete this._listeners[id];
}
removeAllListeners() {
this._listeners = {};
}
/**
* Stops any running animation or tracking. `callback` is invoked with the
* final value after stopping the animation, which is useful for updating
* state to match the animation position with layout.
*/
stopAnimation(callback) {
this.stopTracking();
if (this._animation) {
this._animation.stop();
}
this._animation = null;
if (callback) {
callback(this.__getValue());
}
}
/**
* Interpolates the value before updating the property, e.g. mapping 0-1 to
* 0-10.
*/
interpolate(config) {
return new AnimatedInterpolation(this, Interpolation.create(config));
}
/**
* Typically only used internally, but could be used by a custom Animation
* class.
*/
animate(animation, callback) {
let handle = null;
if (animation.__isInteraction) {
handle = InteractionManager.createInteractionHandle();
}
const previousAnimation = this._animation;
if (this._animation) {
this._animation.stop();
}
this._animation = animation;
animation.start(
this._value,
(value) => {
this._updateValue(value);
},
(result) => {
this._animation = null;
if (handle !== null) {
InteractionManager.clearInteractionHandle(handle);
}
if (callback) {
callback(result);
}
},
previousAnimation
);
}
/**
* Typically only used internally.
*/
stopTracking() {
if (this._tracking) {
this._tracking.__detach();
}
this._tracking = null;
}
/**
* Typically only used internally.
*/
track(tracking) {
this.stopTracking();
this._tracking = tracking;
}
_updateValue(value) {
this._value = value;
_flush(this);
for (const key in this._listeners) {
this._listeners[key]({ value: this.__getValue() });
}
}
}
class AnimatedValueXY extends AnimatedWithChildren {
constructor(valueIn) {
super();
const value = valueIn || { x: 0, y: 0 }; // @flowfixme: shouldn't need `: any`
if (typeof value.x === 'number' && typeof value.y === 'number') {
this.x = new AnimatedValue(value.x);
this.y = new AnimatedValue(value.y);
} else {
invariant(
value.x instanceof AnimatedValue &&
value.y instanceof AnimatedValue,
'AnimatedValueXY must be initalized with an object of numbers or ' +
'AnimatedValues.'
);
this.x = value.x;
this.y = value.y;
}
this._listeners = {};
}
setValue(value) {
this.x.setValue(value.x);
this.y.setValue(value.y);
}
setOffset(offset) {
this.x.setOffset(offset.x);
this.y.setOffset(offset.y);
}
flattenOffset() {
this.x.flattenOffset();
this.y.flattenOffset();
}
__getValue() {
return {
x: this.x.__getValue(),
y: this.y.__getValue(),
};
}
stopAnimation(callback) {
this.x.stopAnimation();
this.y.stopAnimation();
if (callback) {
callback(this.__getValue());
}
}
addListener(callback) {
const id = String(_uniqueId++);
const jointCallback = ({ value }) => {
callback(this.__getValue());
};
this._listeners[id] = {
x: this.x.addListener(jointCallback),
y: this.y.addListener(jointCallback),
};
return id;
}
removeListener(id) {
this.x.removeListener(this._listeners[id].x);
this.y.removeListener(this._listeners[id].y);
delete this._listeners[id];
}
/**
* Converts `{x, y}` into `{left, top}` for use in style, e.g.
*
*```javascript
* style={this.state.anim.getLayout()}
*```
*/
getLayout() {
return {
left: this.x,
top: this.y,
};
}
/**
* Converts `{x, y}` into a useable translation transform, e.g.
*
*```javascript
* style={{
* transform: this.state.anim.getTranslateTransform()
* }}
*```
*/
getTranslateTransform() {
return [
{ translateX: this.x },
{ translateY: this.y }
];
}
}
class AnimatedAddition extends AnimatedWithChildren {
constructor(a, b) {
super();
this._a = a;
this._b = b;
}
__getValue() {
return this._a.__getValue() + this._b.__getValue();
}
interpolate(config) {
return new AnimatedInterpolation(this, Interpolation.create(config));
}
__attach() {
this._a.__addChild(this);
this._b.__addChild(this);
}
__detach() {
this._a.__removeChild(this);
this._b.__removeChild(this);
}
}
class AnimatedMultiplication extends AnimatedWithChildren {
constructor(a, b) {
super();
this._a = a;
this._b = b;
}
__getValue() {
return this._a.__getValue() * this._b.__getValue();
}
interpolate(config) {
return new AnimatedInterpolation(this, Interpolation.create(config));
}
__attach() {
this._a.__addChild(this);
this._b.__addChild(this);
}
__detach() {
this._a.__removeChild(this);
this._b.__removeChild(this);
}
}
class AnimatedTransform extends AnimatedWithChildren {
constructor(transforms) {
super();
this._transforms = transforms;
}
__getValue() {
return this._transforms.map(transform => {
const result = {};
for (const key in transform) {
const value = transform[key];
if (value instanceof Animated) {
result[key] = value.__getValue();
} else {
result[key] = value;
}
}
return result;
});
}
__getAnimatedValue() {
return this._transforms.map(transform => {
const result = {};
for (const key in transform) {
const value = transform[key];
if (value instanceof Animated) {
result[key] = value.__getAnimatedValue();
} else {
// All transform components needed to recompose matrix
result[key] = value;
}
}
return result;
});
}
__attach() {
this._transforms.forEach(transform => {
for (const key in transform) {
const value = transform[key];
if (value instanceof Animated) {
value.__addChild(this);
}
}
});
}
__detach() {
this._transforms.forEach(transform => {
for (const key in transform) {
const value = transform[key];
if (value instanceof Animated) {
value.__removeChild(this);
}
}
});
}
}
class AnimatedStyle extends AnimatedWithChildren {
constructor(style) {
super();
let newStyle;
newStyle = flattenStyle(style) || {};
if (newStyle.transform) {
newStyle = {
...newStyle,
transform: new AnimatedTransform(newStyle.transform),
};
}
this._style = newStyle;
}
__getValue() {
const style = {};
for (const key in this._style) {
const value = this._style[key];
if (value instanceof Animated) {
style[key] = value.__getValue();
} else {
style[key] = value;
}
}
return style;
}
__getAnimatedValue() {
const style = {};
for (const key in this._style) {
const value = this._style[key];
if (value instanceof Animated) {
style[key] = value.__getAnimatedValue();
}
}
return style;
}
__attach() {
for (const key in this._style) {
const value = this._style[key];
if (value instanceof Animated) {
value.__addChild(this);
}
}
}
__detach() {
for (const key in this._style) {
const value = this._style[key];
if (value instanceof Animated) {
value.__removeChild(this);
}
}
}
}
class AnimatedProps extends Animated {
constructor(props, callback) {
super();
this._props = props;
if (this._props.style) {
this._props = {
...this._props,
style: new AnimatedStyle(this._props.style),
};
}
this._callback = callback;
this.__attach();
}
__getValue() {
const props = {};
for (const key in this._props) {
const value = this._props[key];
if (value instanceof Animated) {
props[key] = value.__getValue();
} else {
props[key] = value;
}
}
return props;
}
__getAnimatedValue() {
const props = {};
for (const key in this._props) {
const value = this._props[key];
if (value instanceof Animated) {
props[key] = value.__getAnimatedValue();
}
}
return props;
}
__attach() {
for (const key in this._props) {
const value = this._props[key];
if (value instanceof Animated) {
value.__addChild(this);
}
}
}
__detach() {
for (const key in this._props) {
const value = this._props[key];
if (value instanceof Animated) {
value.__removeChild(this);
}
}
}
update() {
this._callback();
}
}
class AnimatedTracking extends Animated {
constructor(value, parent, animationClass, animationConfig, callback) {
super();
this._value = value;
this._parent = parent;
this._animationClass = animationClass;
this._animationConfig = animationConfig;
this._callback = callback;
this.__attach();
}
__getValue() {
return this._parent.__getValue();
}
__attach() {
this._parent.__addChild(this);
}
__detach() {
this._parent.__removeChild(this);
}
update() {
this._value.animate(new this._animationClass({
...this._animationConfig,
toValue: this._animationConfig.toValue.__getValue(),
}), this._callback);
}
}
function add(a, b) {
return new AnimatedAddition(a, b);
}
function multiply(a, b) {
return new AnimatedMultiplication(a, b);
}
function parallel(animations, config) {
let doneCount = 0;
// Make sure we only call stop() at most once for each animation
const hasEnded = {};
const stopTogether = !(config && config.stopTogether === false);
const result = {
start(callback) {
if (doneCount === animations.length) {
if (callback) {
callback({ finished: true });
}
return;
}
animations.forEach((animation, idx) => {
const cb = function (endResult) {
hasEnded[idx] = true;
doneCount++;
if (doneCount === animations.length) {
doneCount = 0;
if (callback) {
callback(endResult);
}
return;
}
if (!endResult.finished && stopTogether) {
result.stop();
}
};
if (!animation) {
cb({ finished: true });
} else {
animation.start(cb);
}
});
},
stop() {
animations.forEach((animation, idx) => {
if (!hasEnded[idx]) {
animation.stop();
}
hasEnded[idx] = true;
});
}
};
return result;
}
function maybeVectorAnim(value, config, anim) {
if (value instanceof AnimatedValueXY) {
const configX = { ...config };
const configY = { ...config };
for (const key in config) {
const { x, y } = config[key];
if (x !== undefined && y !== undefined) {
configX[key] = x;
configY[key] = y;
}
}
const aX = anim(value.x, configX);
const aY = anim(value.y, configY);
// We use `stopTogether: false` here because otherwise tracking will break
// because the second animation will get stopped before it can update.
return parallel([aX, aY], { stopTogether: false });
}
return null;
}
function spring(value, config) {
return maybeVectorAnim(value, config, spring) || {
start(callback) {
const singleValue = value;
const singleConfig = config;
singleValue.stopTracking();
if (config.toValue instanceof Animated) {
singleValue.track(new AnimatedTracking(
singleValue,
config.toValue,
SpringAnimation,
singleConfig,
callback
));
} else {
singleValue.animate(new SpringAnimation(singleConfig), callback);
}
},
stop() {
value.stopAnimation();
},
};
}
function timing(value, config) {
return maybeVectorAnim(value, config, timing) || {
start(callback) {
const singleValue = value;
const singleConfig = config;
singleValue.stopTracking();
if (config.toValue instanceof Animated) {
singleValue.track(new AnimatedTracking(
singleValue,
config.toValue,
TimingAnimation,
singleConfig,
callback
));
} else {
singleValue.animate(new TimingAnimation(singleConfig), callback);
}
},
stop() {
value.stopAnimation();
},
};
}
function decay(value, config) {
return maybeVectorAnim(value, config, decay) || {
start(callback) {
const singleValue = value;
const singleConfig = config;
singleValue.stopTracking();
singleValue.animate(new DecayAnimation(singleConfig), callback);
},
stop() {
value.stopAnimation();
},
};
}
function sequence(animations) {
let current = 0;
return {
start(callback) {
const onComplete = function (result) {
if (!result.finished) {
if (callback) {
callback(result);
}
return;
}
current++;
if (current === animations.length) {
if (callback) {
callback(result);
}
return;
}
animations[current].start(onComplete);
};
if (animations.length === 0) {
if (callback) {
callback({ finished: true });
}
} else {
animations[current].start(onComplete);
}
},
stop() {
if (current < animations.length) {
animations[current].stop();
}
}
};
}
function delay(time) {
// Would be nice to make a specialized implementation
return timing(new AnimatedValue(0), { toValue: 0, delay: time, duration: 0 });
}
function stagger(time, animations) {
return parallel(animations.map(function (animation, i) {
return sequence([
delay(time * i),
animation,
]);
}));
}
function event(argMapping, config) {
return function (...args) {
const traverse = function (recMapping, recEvt, key) {
if (typeof recEvt === 'number') {
invariant(
recMapping instanceof AnimatedValue,
'Bad mapping of type ' + typeof recMapping + ' for key ' + key +
', event value must map to AnimatedValue'
);
recMapping.setValue(recEvt);
return;
}
invariant(
typeof recMapping === 'object',
'Bad mapping of type ' + typeof recMapping + ' for key ' + key
);
invariant(
typeof recEvt === 'object',
'Bad event of type ' + typeof recEvt + ' for key ' + key
);
for (const i in recMapping) {
traverse(recMapping[i], recEvt[i], i);
}
};
argMapping.forEach((mapping, idx) => {
traverse(mapping, args[idx], 'arg' + idx);
});
if (config && config.listener) {
config.listener.apply(null, args);
}
};
}
const AnimatedImplementation = {
Value: AnimatedValue,
ValueXY: AnimatedValueXY,
decay,
timing,
spring,
add,
multiply,
sequence,
parallel,
stagger,
event,
__PropsOnlyForTests: AnimatedProps,
__Animated: Animated,
__Animation: Animation,
__AnimatedWithChildren: AnimatedWithChildren,
__AnimatedStyle: AnimatedStyle,
};
module.exports = AnimatedImplementation;