Skip to content

Commit

Permalink
Allow animated output range.
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanVann committed Apr 25, 2018
1 parent 4a80210 commit cfcdc81
Show file tree
Hide file tree
Showing 18 changed files with 458 additions and 155 deletions.
41 changes: 41 additions & 0 deletions Libraries/Animated/src/AnimatedImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,34 @@ import type {TimingAnimationConfig} from './animations/TimingAnimation';
import type {DecayAnimationConfig} from './animations/DecayAnimation';
import type {SpringAnimationConfig} from './animations/SpringAnimation';
import type {Mapping, EventConfig} from './AnimatedEvent';
import type {InterpolationConfigType} from './nodes/AnimatedInterpolation';

const interpolateMethod = function(
config: InterpolationConfigType,
): AnimatedInterpolation {
console.warn(
'The animation.interpolate(config) method will be removed from animated nodes in favour of Animated.interpolate(animation, config).',
);
return new AnimatedInterpolation(this, config);
};

// To avoid some code duplication and a circular dependency we
// are adding the interpolate method directly onto these prototypes.
// This should eventually be removed.
//$FlowFixMe
AnimatedAddition.prototype.interpolate = interpolateMethod;
//$FlowFixMe
AnimatedDiffClamp.prototype.interpolate = interpolateMethod;
//$FlowFixMe
AnimatedDivision.prototype.interpolate = interpolateMethod;
//$FlowFixMe
AnimatedInterpolation.prototype.interpolate = interpolateMethod;
//$FlowFixMe
AnimatedModulo.prototype.interpolate = interpolateMethod;
//$FlowFixMe
AnimatedMultiplication.prototype.interpolate = interpolateMethod;
//$FlowFixMe
AnimatedValue.prototype.interpolate = interpolateMethod;

export type CompositeAnimation = {
start: (callback?: ?EndCallback) => void,
Expand Down Expand Up @@ -233,6 +261,13 @@ const timing = function(
);
};

const interpolate = function(
value: AnimatedValue,
config: InterpolationConfigType,
): AnimatedInterpolation {
return new AnimatedInterpolation(value, config);
};

const decay = function(
value: AnimatedValue | AnimatedValueXY,
config: DecayAnimationConfig,
Expand Down Expand Up @@ -546,6 +581,12 @@ module.exports = {
*/
Node: AnimatedNode,

/**
* Interpolates the value before updating the property, e.g. mapping 0-1 to
* 0-10.
*/
interpolate,

/**
* Animates a value from an initial velocity to zero based on a decay
* coefficient.
Expand Down
3 changes: 2 additions & 1 deletion Libraries/Animated/src/__tests__/AnimatedNative-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -475,8 +475,9 @@ describe('Native Animated', () => {
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(expect.any(Number), {
type: 'interpolation',
parent: expect.any(Number),
inputRange: [10, 20],
outputRange: [0, 1],
outputRange: [expect.any(Number), expect.any(Number)],
extrapolateLeft: 'extend',
extrapolateRight: 'extend',
});
Expand Down
7 changes: 0 additions & 7 deletions Libraries/Animated/src/nodes/AnimatedAddition.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@
*/
'use strict';

const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');

import type {InterpolationConfigType} from './AnimatedInterpolation';

class AnimatedAddition extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
Expand All @@ -37,10 +34,6 @@ class AnimatedAddition extends AnimatedWithChildren {
return this._a.__getValue() + this._b.__getValue();
}

interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}

__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
Expand Down
7 changes: 0 additions & 7 deletions Libraries/Animated/src/nodes/AnimatedDiffClamp.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@
*/
'use strict';

const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');

import type {InterpolationConfigType} from './AnimatedInterpolation';

class AnimatedDiffClamp extends AnimatedWithChildren {
_a: AnimatedNode;
_min: number;
Expand All @@ -37,10 +34,6 @@ class AnimatedDiffClamp extends AnimatedWithChildren {
super.__makeNative();
}

interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}

__getValue(): number {
const value = this._a.__getValue();
const diff = value - this._lastValue;
Expand Down
7 changes: 0 additions & 7 deletions Libraries/Animated/src/nodes/AnimatedDivision.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@
*/
'use strict';

const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');

import type {InterpolationConfigType} from './AnimatedInterpolation';

class AnimatedDivision extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
Expand All @@ -42,10 +39,6 @@ class AnimatedDivision extends AnimatedWithChildren {
return a / b;
}

interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}

__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
Expand Down
91 changes: 62 additions & 29 deletions Libraries/Animated/src/nodes/AnimatedInterpolation.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'use strict';

const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');

Expand All @@ -26,7 +27,7 @@ export type InterpolationConfigType = {
* detected during the deployment of v0.38.0. To see the error, remove this
* comment and run flow
*/
outputRange: Array<number> | Array<string>,
outputRange: Array<number> | Array<string> | Array<AnimatedNode>,
easing?: (input: number) => number,
extrapolate?: ExtrapolateType,
extrapolateLeft?: ExtrapolateType,
Expand All @@ -46,7 +47,7 @@ function createInterpolation(
return createInterpolationFromStringOutputRange(config);
}

const outputRange: Array<number> = (config.outputRange: any);
const outputRange: Array<number> | Array<AnimatedNode> = config.outputRange;
checkInfiniteRange('outputRange', outputRange);

const inputRange = config.inputRange;
Expand Down Expand Up @@ -85,12 +86,20 @@ function createInterpolation(
);

const range = findRange(input, inputRange);
const outputStart: number | AnimatedNode = outputRange[range];
const outputEnd: number | AnimatedNode = outputRange[range + 1];
const outputStartValue =
outputStart instanceof AnimatedNode
? outputStart.__getValue()
: outputStart;
const outputEndValue =
outputEnd instanceof AnimatedNode ? outputEnd.__getValue() : outputEnd;
return interpolate(
input,
inputRange[range],
inputRange[range + 1],
outputRange[range],
outputRange[range + 1],
outputStartValue,
outputEndValue,
easing,
extrapolateLeft,
extrapolateRight,
Expand Down Expand Up @@ -291,7 +300,7 @@ function checkValidInputRange(arr: Array<number>) {
}
}

function checkInfiniteRange(name: string, arr: Array<number>) {
function checkInfiniteRange(name: string, arr: Array<any>) {
invariant(arr.length >= 2, name + ' must have at least 2 elements');
invariant(
arr.length !== 2 || arr[0] !== -Infinity || arr[1] !== Infinity,
Expand All @@ -311,17 +320,24 @@ class AnimatedInterpolation extends AnimatedWithChildren {

_parent: AnimatedNode;
_config: InterpolationConfigType;
_transformedOutputRange: Array<AnimatedNode>;
_interpolation: (input: number) => number | string;

constructor(parent: AnimatedNode, config: InterpolationConfigType) {
super();
this._parent = parent;
this._config = config;
this._transformedOutputRange = this.__transformOutputRangeToAnimatedValues(
config.outputRange,
);
this._interpolation = createInterpolation(config);
}

__makeNative() {
__makeNative(): void {
this._parent.__makeNative();
this._transformedOutputRange.forEach(function(value) {
value.__makeNative();
});
super.__makeNative();
}

Expand All @@ -334,37 +350,54 @@ class AnimatedInterpolation extends AnimatedWithChildren {
return this._interpolation(parentValue);
}

interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}

__attach(): void {
this._parent.__addChild(this);
const that = this;
this._parent.__addChild(that);
this._transformedOutputRange.forEach(function(value) {
value.__addChild(that);
});
}

__detach(): void {
this._parent.__removeChild(this);
const that = this;
this._parent.__removeChild(that);
this._transformedOutputRange.forEach(function(value) {
value.__removeChild(that);
});
super.__detach();
}

__transformDataType(range: Array<any>) {
// Change the string array type to number array
// So we can reuse the same logic in iOS and Android platform
/* $FlowFixMe(>=0.70.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.70 was deployed. To see the error delete this
* comment and run Flow. */
__transformOutputRangeToAnimatedValues(
range: Array<number | string | AnimatedNode>,
): Array<AnimatedNode> {
return range.map(function(value) {
if (typeof value !== 'string') {
return value;
}
if (/deg$/.test(value)) {
if (typeof value === 'string' && /deg$/.test(value)) {
const degrees = parseFloat(value) || 0;
// Radians.
const radians = degrees * Math.PI / 180.0;
return radians;
} else {
// Assume radians
return parseFloat(value) || 0;
return new AnimatedValue(radians);
}
if (typeof value === 'string') {
// Assume radians.
const radians = parseFloat(value) || 0;
return new AnimatedValue(radians);
}
if (typeof value === 'number') {
// Just a plain number value.
return new AnimatedValue(value);
}
if (value instanceof AnimatedNode) {
return value;
}
throw new Error('Incompatible type passed to outputRange.');
});
}

__outputRangeToTags(range: Array<AnimatedNode>): Array<number> {
return range.map(function(value) {
const tag = value.__getNativeTag();
invariant(tag, 'There must be a native tag for this value.');
return tag;
});
}

Expand All @@ -374,14 +407,14 @@ class AnimatedInterpolation extends AnimatedWithChildren {
}

return {
type: 'interpolation',
parent: this._parent.__getNativeTag(),
inputRange: this._config.inputRange,
// Only the `outputRange` can contain strings so we don't need to transform `inputRange` here
outputRange: this.__transformDataType(this._config.outputRange),
outputRange: this.__outputRangeToTags(this._transformedOutputRange),
extrapolateLeft:
this._config.extrapolateLeft || this._config.extrapolate || 'extend',
extrapolateRight:
this._config.extrapolateRight || this._config.extrapolate || 'extend',
type: 'interpolation',
};
}
}
Expand Down
7 changes: 0 additions & 7 deletions Libraries/Animated/src/nodes/AnimatedModulo.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@
*/
'use strict';

const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');

import type {InterpolationConfigType} from './AnimatedInterpolation';

class AnimatedModulo extends AnimatedWithChildren {
_a: AnimatedNode;
_modulus: number;
Expand All @@ -37,10 +34,6 @@ class AnimatedModulo extends AnimatedWithChildren {
);
}

interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}

__attach(): void {
this._a.__addChild(this);
}
Expand Down
7 changes: 0 additions & 7 deletions Libraries/Animated/src/nodes/AnimatedMultiplication.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@
*/
'use strict';

const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');

import type {InterpolationConfigType} from './AnimatedInterpolation';

class AnimatedMultiplication extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
Expand All @@ -37,10 +34,6 @@ class AnimatedMultiplication extends AnimatedWithChildren {
return this._a.__getValue() * this._b.__getValue();
}

interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}

__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
Expand Down
16 changes: 16 additions & 0 deletions Libraries/Animated/src/nodes/AnimatedNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ class AnimatedNode {
return [];
}

/**
* Deprecated - Use `Animated.interpolate(animation, config)` instead.
*
* Interpolates the value before updating the property, e.g. mapping 0-1 to
* 0-10. Not available on all node types.
*
* @deprecated
*/
interpolate(config: any): AnimatedNode {
throw new Error(
'This node type does not implement an interpolate method,' +
' the interpolate method will be removed from all nodes' +
' in favour of Animated.interpolate(animation, config).',
);
}

/* Methods and props used by native Animated impl */
__isNative: boolean;
__nativeTag: ?number;
Expand Down
Loading

0 comments on commit cfcdc81

Please sign in to comment.