Skip to content

Commit cfcdc81

Browse files
committed
Allow animated output range.
1 parent 4a80210 commit cfcdc81

18 files changed

Lines changed: 458 additions & 155 deletions

Libraries/Animated/src/AnimatedImplementation.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,34 @@ import type {TimingAnimationConfig} from './animations/TimingAnimation';
3939
import type {DecayAnimationConfig} from './animations/DecayAnimation';
4040
import type {SpringAnimationConfig} from './animations/SpringAnimation';
4141
import type {Mapping, EventConfig} from './AnimatedEvent';
42+
import type {InterpolationConfigType} from './nodes/AnimatedInterpolation';
43+
44+
const interpolateMethod = function(
45+
config: InterpolationConfigType,
46+
): AnimatedInterpolation {
47+
console.warn(
48+
'The animation.interpolate(config) method will be removed from animated nodes in favour of Animated.interpolate(animation, config).',
49+
);
50+
return new AnimatedInterpolation(this, config);
51+
};
52+
53+
// To avoid some code duplication and a circular dependency we
54+
// are adding the interpolate method directly onto these prototypes.
55+
// This should eventually be removed.
56+
//$FlowFixMe
57+
AnimatedAddition.prototype.interpolate = interpolateMethod;
58+
//$FlowFixMe
59+
AnimatedDiffClamp.prototype.interpolate = interpolateMethod;
60+
//$FlowFixMe
61+
AnimatedDivision.prototype.interpolate = interpolateMethod;
62+
//$FlowFixMe
63+
AnimatedInterpolation.prototype.interpolate = interpolateMethod;
64+
//$FlowFixMe
65+
AnimatedModulo.prototype.interpolate = interpolateMethod;
66+
//$FlowFixMe
67+
AnimatedMultiplication.prototype.interpolate = interpolateMethod;
68+
//$FlowFixMe
69+
AnimatedValue.prototype.interpolate = interpolateMethod;
4270

4371
export type CompositeAnimation = {
4472
start: (callback?: ?EndCallback) => void,
@@ -233,6 +261,13 @@ const timing = function(
233261
);
234262
};
235263

264+
const interpolate = function(
265+
value: AnimatedValue,
266+
config: InterpolationConfigType,
267+
): AnimatedInterpolation {
268+
return new AnimatedInterpolation(value, config);
269+
};
270+
236271
const decay = function(
237272
value: AnimatedValue | AnimatedValueXY,
238273
config: DecayAnimationConfig,
@@ -546,6 +581,12 @@ module.exports = {
546581
*/
547582
Node: AnimatedNode,
548583

584+
/**
585+
* Interpolates the value before updating the property, e.g. mapping 0-1 to
586+
* 0-10.
587+
*/
588+
interpolate,
589+
549590
/**
550591
* Animates a value from an initial velocity to zero based on a decay
551592
* coefficient.

Libraries/Animated/src/__tests__/AnimatedNative-test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,8 +475,9 @@ describe('Native Animated', () => {
475475
expect(nativeAnimatedModule.createAnimatedNode)
476476
.toBeCalledWith(expect.any(Number), {
477477
type: 'interpolation',
478+
parent: expect.any(Number),
478479
inputRange: [10, 20],
479-
outputRange: [0, 1],
480+
outputRange: [expect.any(Number), expect.any(Number)],
480481
extrapolateLeft: 'extend',
481482
extrapolateRight: 'extend',
482483
});

Libraries/Animated/src/nodes/AnimatedAddition.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@
1010
*/
1111
'use strict';
1212

13-
const AnimatedInterpolation = require('./AnimatedInterpolation');
1413
const AnimatedNode = require('./AnimatedNode');
1514
const AnimatedValue = require('./AnimatedValue');
1615
const AnimatedWithChildren = require('./AnimatedWithChildren');
1716

18-
import type {InterpolationConfigType} from './AnimatedInterpolation';
19-
2017
class AnimatedAddition extends AnimatedWithChildren {
2118
_a: AnimatedNode;
2219
_b: AnimatedNode;
@@ -37,10 +34,6 @@ class AnimatedAddition extends AnimatedWithChildren {
3734
return this._a.__getValue() + this._b.__getValue();
3835
}
3936

40-
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
41-
return new AnimatedInterpolation(this, config);
42-
}
43-
4437
__attach(): void {
4538
this._a.__addChild(this);
4639
this._b.__addChild(this);

Libraries/Animated/src/nodes/AnimatedDiffClamp.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@
1010
*/
1111
'use strict';
1212

13-
const AnimatedInterpolation = require('./AnimatedInterpolation');
1413
const AnimatedNode = require('./AnimatedNode');
1514
const AnimatedWithChildren = require('./AnimatedWithChildren');
1615

17-
import type {InterpolationConfigType} from './AnimatedInterpolation';
18-
1916
class AnimatedDiffClamp extends AnimatedWithChildren {
2017
_a: AnimatedNode;
2118
_min: number;
@@ -37,10 +34,6 @@ class AnimatedDiffClamp extends AnimatedWithChildren {
3734
super.__makeNative();
3835
}
3936

40-
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
41-
return new AnimatedInterpolation(this, config);
42-
}
43-
4437
__getValue(): number {
4538
const value = this._a.__getValue();
4639
const diff = value - this._lastValue;

Libraries/Animated/src/nodes/AnimatedDivision.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@
1010
*/
1111
'use strict';
1212

13-
const AnimatedInterpolation = require('./AnimatedInterpolation');
1413
const AnimatedNode = require('./AnimatedNode');
1514
const AnimatedValue = require('./AnimatedValue');
1615
const AnimatedWithChildren = require('./AnimatedWithChildren');
1716

18-
import type {InterpolationConfigType} from './AnimatedInterpolation';
19-
2017
class AnimatedDivision extends AnimatedWithChildren {
2118
_a: AnimatedNode;
2219
_b: AnimatedNode;
@@ -42,10 +39,6 @@ class AnimatedDivision extends AnimatedWithChildren {
4239
return a / b;
4340
}
4441

45-
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
46-
return new AnimatedInterpolation(this, config);
47-
}
48-
4942
__attach(): void {
5043
this._a.__addChild(this);
5144
this._b.__addChild(this);

Libraries/Animated/src/nodes/AnimatedInterpolation.js

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
'use strict';
1313

1414
const AnimatedNode = require('./AnimatedNode');
15+
const AnimatedValue = require('./AnimatedValue');
1516
const AnimatedWithChildren = require('./AnimatedWithChildren');
1617
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
1718

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

49-
const outputRange: Array<number> = (config.outputRange: any);
50+
const outputRange: Array<number> | Array<AnimatedNode> = config.outputRange;
5051
checkInfiniteRange('outputRange', outputRange);
5152

5253
const inputRange = config.inputRange;
@@ -85,12 +86,20 @@ function createInterpolation(
8586
);
8687

8788
const range = findRange(input, inputRange);
89+
const outputStart: number | AnimatedNode = outputRange[range];
90+
const outputEnd: number | AnimatedNode = outputRange[range + 1];
91+
const outputStartValue =
92+
outputStart instanceof AnimatedNode
93+
? outputStart.__getValue()
94+
: outputStart;
95+
const outputEndValue =
96+
outputEnd instanceof AnimatedNode ? outputEnd.__getValue() : outputEnd;
8897
return interpolate(
8998
input,
9099
inputRange[range],
91100
inputRange[range + 1],
92-
outputRange[range],
93-
outputRange[range + 1],
101+
outputStartValue,
102+
outputEndValue,
94103
easing,
95104
extrapolateLeft,
96105
extrapolateRight,
@@ -291,7 +300,7 @@ function checkValidInputRange(arr: Array<number>) {
291300
}
292301
}
293302

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

312321
_parent: AnimatedNode;
313322
_config: InterpolationConfigType;
323+
_transformedOutputRange: Array<AnimatedNode>;
314324
_interpolation: (input: number) => number | string;
315325

316326
constructor(parent: AnimatedNode, config: InterpolationConfigType) {
317327
super();
318328
this._parent = parent;
319329
this._config = config;
330+
this._transformedOutputRange = this.__transformOutputRangeToAnimatedValues(
331+
config.outputRange,
332+
);
320333
this._interpolation = createInterpolation(config);
321334
}
322335

323-
__makeNative() {
336+
__makeNative(): void {
324337
this._parent.__makeNative();
338+
this._transformedOutputRange.forEach(function(value) {
339+
value.__makeNative();
340+
});
325341
super.__makeNative();
326342
}
327343

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

337-
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
338-
return new AnimatedInterpolation(this, config);
339-
}
340-
341353
__attach(): void {
342-
this._parent.__addChild(this);
354+
const that = this;
355+
this._parent.__addChild(that);
356+
this._transformedOutputRange.forEach(function(value) {
357+
value.__addChild(that);
358+
});
343359
}
344360

345361
__detach(): void {
346-
this._parent.__removeChild(this);
362+
const that = this;
363+
this._parent.__removeChild(that);
364+
this._transformedOutputRange.forEach(function(value) {
365+
value.__removeChild(that);
366+
});
347367
super.__detach();
348368
}
349369

350-
__transformDataType(range: Array<any>) {
351-
// Change the string array type to number array
352-
// So we can reuse the same logic in iOS and Android platform
353-
/* $FlowFixMe(>=0.70.0 site=react_native_fb) This comment suppresses an
354-
* error found when Flow v0.70 was deployed. To see the error delete this
355-
* comment and run Flow. */
370+
__transformOutputRangeToAnimatedValues(
371+
range: Array<number | string | AnimatedNode>,
372+
): Array<AnimatedNode> {
356373
return range.map(function(value) {
357-
if (typeof value !== 'string') {
358-
return value;
359-
}
360-
if (/deg$/.test(value)) {
374+
if (typeof value === 'string' && /deg$/.test(value)) {
361375
const degrees = parseFloat(value) || 0;
376+
// Radians.
362377
const radians = degrees * Math.PI / 180.0;
363-
return radians;
364-
} else {
365-
// Assume radians
366-
return parseFloat(value) || 0;
378+
return new AnimatedValue(radians);
379+
}
380+
if (typeof value === 'string') {
381+
// Assume radians.
382+
const radians = parseFloat(value) || 0;
383+
return new AnimatedValue(radians);
367384
}
385+
if (typeof value === 'number') {
386+
// Just a plain number value.
387+
return new AnimatedValue(value);
388+
}
389+
if (value instanceof AnimatedNode) {
390+
return value;
391+
}
392+
throw new Error('Incompatible type passed to outputRange.');
393+
});
394+
}
395+
396+
__outputRangeToTags(range: Array<AnimatedNode>): Array<number> {
397+
return range.map(function(value) {
398+
const tag = value.__getNativeTag();
399+
invariant(tag, 'There must be a native tag for this value.');
400+
return tag;
368401
});
369402
}
370403

@@ -374,14 +407,14 @@ class AnimatedInterpolation extends AnimatedWithChildren {
374407
}
375408

376409
return {
410+
type: 'interpolation',
411+
parent: this._parent.__getNativeTag(),
377412
inputRange: this._config.inputRange,
378-
// Only the `outputRange` can contain strings so we don't need to transform `inputRange` here
379-
outputRange: this.__transformDataType(this._config.outputRange),
413+
outputRange: this.__outputRangeToTags(this._transformedOutputRange),
380414
extrapolateLeft:
381415
this._config.extrapolateLeft || this._config.extrapolate || 'extend',
382416
extrapolateRight:
383417
this._config.extrapolateRight || this._config.extrapolate || 'extend',
384-
type: 'interpolation',
385418
};
386419
}
387420
}

Libraries/Animated/src/nodes/AnimatedModulo.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@
1010
*/
1111
'use strict';
1212

13-
const AnimatedInterpolation = require('./AnimatedInterpolation');
1413
const AnimatedNode = require('./AnimatedNode');
1514
const AnimatedWithChildren = require('./AnimatedWithChildren');
1615

17-
import type {InterpolationConfigType} from './AnimatedInterpolation';
18-
1916
class AnimatedModulo extends AnimatedWithChildren {
2017
_a: AnimatedNode;
2118
_modulus: number;
@@ -37,10 +34,6 @@ class AnimatedModulo extends AnimatedWithChildren {
3734
);
3835
}
3936

40-
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
41-
return new AnimatedInterpolation(this, config);
42-
}
43-
4437
__attach(): void {
4538
this._a.__addChild(this);
4639
}

Libraries/Animated/src/nodes/AnimatedMultiplication.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@
1010
*/
1111
'use strict';
1212

13-
const AnimatedInterpolation = require('./AnimatedInterpolation');
1413
const AnimatedNode = require('./AnimatedNode');
1514
const AnimatedValue = require('./AnimatedValue');
1615
const AnimatedWithChildren = require('./AnimatedWithChildren');
1716

18-
import type {InterpolationConfigType} from './AnimatedInterpolation';
19-
2017
class AnimatedMultiplication extends AnimatedWithChildren {
2118
_a: AnimatedNode;
2219
_b: AnimatedNode;
@@ -37,10 +34,6 @@ class AnimatedMultiplication extends AnimatedWithChildren {
3734
return this._a.__getValue() * this._b.__getValue();
3835
}
3936

40-
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
41-
return new AnimatedInterpolation(this, config);
42-
}
43-
4437
__attach(): void {
4538
this._a.__addChild(this);
4639
this._b.__addChild(this);

Libraries/Animated/src/nodes/AnimatedNode.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ class AnimatedNode {
3434
return [];
3535
}
3636

37+
/**
38+
* Deprecated - Use `Animated.interpolate(animation, config)` instead.
39+
*
40+
* Interpolates the value before updating the property, e.g. mapping 0-1 to
41+
* 0-10. Not available on all node types.
42+
*
43+
* @deprecated
44+
*/
45+
interpolate(config: any): AnimatedNode {
46+
throw new Error(
47+
'This node type does not implement an interpolate method,' +
48+
' the interpolate method will be removed from all nodes' +
49+
' in favour of Animated.interpolate(animation, config).',
50+
);
51+
}
52+
3753
/* Methods and props used by native Animated impl */
3854
__isNative: boolean;
3955
__nativeTag: ?number;

0 commit comments

Comments
 (0)