Introduced AnimatedDivision

Summary:
Combining 2 animated values via addition, multiplication, and modulo are already supported, and this adds another one: division.
There are some cases where an animated value needs to invert (1 / x) another animated value for calculation. An example is inverting a scale (2x --> 0.5x), e.g.:

```
const a = Animated.Value(1);
const b = Animated.divide(1, a);

Animated.spring(a, {
  toValue: 2,
}).start();
```

`b` will then follow `a`'s spring animation and produce the value of `1 / a`.

The basic usage is like this:

```
<Animated.View style={{transform: [{scale: a}]}}>
  <Animated.Image style={{transform: [{scale: b}]}} />
<Animated.View>
```

In this example, the inner image won't get stretched at all because the parent's scaling gets cancelled out.

Also added this to native animated implementation.

Reviewed By: foghina, mmmulani

Differential Revision: D3922891

fbshipit-source-id: 32508956c4b65b2deb7574d50a10c85b4809b961
This commit is contained in:
Kevin Gozali 2016-09-26 16:32:27 -07:00 committed by Facebook Github Bot 6
parent 9af620bc33
commit 0a0dd30c6a
7 changed files with 184 additions and 7 deletions

View File

@ -1136,6 +1136,54 @@ class AnimatedAddition extends AnimatedWithChildren {
}
}
class AnimatedDivision extends AnimatedWithChildren {
_a: Animated;
_b: Animated;
constructor(a: Animated | number, b: Animated | number) {
super();
this._a = typeof a === 'number' ? new AnimatedValue(a) : a;
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
}
__makeNative() {
super.__makeNative();
this._a.__makeNative();
this._b.__makeNative();
}
__getValue(): number {
const a = this._a.__getValue();
const b = this._b.__getValue();
if (b === 0) {
console.error('Detected division by zero in AnimatedDivision');
}
return a / b;
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
}
__detach(): void {
this._a.__removeChild(this);
this._b.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'division',
input: [this._a.__getNativeTag(), this._b.__getNativeTag()],
};
}
}
class AnimatedMultiplication extends AnimatedWithChildren {
_a: Animated;
_b: Animated;
@ -1795,15 +1843,22 @@ type CompositeAnimation = {
};
var add = function(
a: Animated,
b: Animated
a: Animated | number,
b: Animated | number,
): AnimatedAddition {
return new AnimatedAddition(a, b);
};
var divide = function(
a: Animated | number,
b: Animated | number,
): AnimatedDivision {
return new AnimatedDivision(a, b);
};
var multiply = function(
a: Animated,
b: Animated
a: Animated | number,
b: Animated | number,
): AnimatedMultiplication {
return new AnimatedMultiplication(a, b);
};
@ -2295,6 +2350,13 @@ module.exports = {
* together.
*/
add,
/**
* Creates a new Animated value composed by dividing the first Animated value
* by the second Animated value.
*/
divide,
/**
* Creates a new Animated value composed from two Animated values multiplied
* together.

View File

@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTValueAnimatedNode.h"
@interface RCTDivisionAnimatedNode : RCTValueAnimatedNode
@end

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTDivisionAnimatedNode.h"
#import "RCTLog.h"
@implementation RCTDivisionAnimatedNode
- (void)performUpdate
{
[super performUpdate];
NSArray<NSNumber *> *inputNodes = self.config[@"input"];
if (inputNodes.count > 1) {
RCTValueAnimatedNode *parent1 = (RCTValueAnimatedNode *)self.parentNodes[inputNodes[0]];
RCTValueAnimatedNode *parent2 = (RCTValueAnimatedNode *)self.parentNodes[inputNodes[1]];
if ([parent1 isKindOfClass:[RCTValueAnimatedNode class]] &&
[parent2 isKindOfClass:[RCTValueAnimatedNode class]]) {
if (parent2.value == 0) {
RCTLogError(@"Detected a division by zero in Animated.divide node");
return;
}
self.value = parent1.value / parent2.value;
}
}
}
@end

View File

@ -19,8 +19,9 @@
13E501EE1D07A6C9005F35D8 /* RCTStyleAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E31D07A6C9005F35D8 /* RCTStyleAnimatedNode.m */; };
13E501EF1D07A6C9005F35D8 /* RCTTransformAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E51D07A6C9005F35D8 /* RCTTransformAnimatedNode.m */; };
13E501F01D07A6C9005F35D8 /* RCTValueAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E71D07A6C9005F35D8 /* RCTValueAnimatedNode.m */; };
94DAE3F91D7334A70059942F /* RCTModuloAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */; };
193F64F41D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 193F64F31D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m */; };
5C9894951D999639008027DB /* RCTDivisionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */; };
94DAE3F91D7334A70059942F /* RCTModuloAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -61,10 +62,12 @@
13E501E51D07A6C9005F35D8 /* RCTTransformAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTransformAnimatedNode.m; sourceTree = "<group>"; };
13E501E61D07A6C9005F35D8 /* RCTValueAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTValueAnimatedNode.h; sourceTree = "<group>"; };
13E501E71D07A6C9005F35D8 /* RCTValueAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTValueAnimatedNode.m; sourceTree = "<group>"; };
94DAE3F71D7334A70059942F /* RCTModuloAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuloAnimatedNode.h; sourceTree = "<group>"; };
94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuloAnimatedNode.m; sourceTree = "<group>"; };
193F64F21D776EC6004D1CAA /* RCTDiffClampAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDiffClampAnimatedNode.h; sourceTree = "<group>"; };
193F64F31D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDiffClampAnimatedNode.m; sourceTree = "<group>"; };
5C9894931D999639008027DB /* RCTDivisionAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDivisionAnimatedNode.h; sourceTree = "<group>"; };
5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDivisionAnimatedNode.m; sourceTree = "<group>"; };
94DAE3F71D7334A70059942F /* RCTModuloAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuloAnimatedNode.h; sourceTree = "<group>"; };
94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuloAnimatedNode.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -89,6 +92,8 @@
13E501D51D07A6C9005F35D8 /* Nodes */ = {
isa = PBXGroup;
children = (
5C9894931D999639008027DB /* RCTDivisionAnimatedNode.h */,
5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */,
193F64F21D776EC6004D1CAA /* RCTDiffClampAnimatedNode.h */,
193F64F31D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m */,
13E501D61D07A6C9005F35D8 /* RCTAdditionAnimatedNode.h */,
@ -196,6 +201,7 @@
13E501E91D07A6C9005F35D8 /* RCTAnimatedNode.m in Sources */,
13E501EB1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m in Sources */,
13E501E81D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m in Sources */,
5C9894951D999639008027DB /* RCTDivisionAnimatedNode.m in Sources */,
13E501EA1D07A6C9005F35D8 /* RCTAnimationDriverNode.m in Sources */,
13E501EF1D07A6C9005F35D8 /* RCTTransformAnimatedNode.m in Sources */,
13E501D41D07A644005F35D8 /* RCTViewPropertyMapper.m in Sources */,

View File

@ -16,6 +16,7 @@
#import "RCTInterpolationAnimatedNode.h"
#import "RCTLog.h"
#import "RCTDiffClampAnimatedNode.h"
#import "RCTDivisionAnimatedNode.h"
#import "RCTModuloAnimatedNode.h"
#import "RCTMultiplicationAnimatedNode.h"
#import "RCTPropsAnimatedNode.h"
@ -71,6 +72,7 @@ RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag
@"interpolation" : [RCTInterpolationAnimatedNode class],
@"addition" : [RCTAdditionAnimatedNode class],
@"diffclamp": [RCTDiffClampAnimatedNode class],
@"division" : [RCTDivisionAnimatedNode class],
@"multiplication" : [RCTMultiplicationAnimatedNode class],
@"modulus" : [RCTModuloAnimatedNode class],
@"transform" : [RCTTransformAnimatedNode class]};

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.animated;
import com.facebook.react.bridge.JSApplicationCausedNativeException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
/**
* Animated node which takes two or more value node as an input and outputs an in-order
* division of their values.
*/
/*package*/ class DivisionAnimatedNode extends ValueAnimatedNode {
private final NativeAnimatedNodesManager mNativeAnimatedNodesManager;
private final int[] mInputNodes;
public DivisionAnimatedNode(
ReadableMap config,
NativeAnimatedNodesManager nativeAnimatedNodesManager) {
mNativeAnimatedNodesManager = nativeAnimatedNodesManager;
ReadableArray inputNodes = config.getArray("input");
mInputNodes = new int[inputNodes.size()];
for (int i = 0; i < mInputNodes.length; i++) {
mInputNodes[i] = inputNodes.getInt(i);
}
}
@Override
public void update() {
for (int i = 0; i < mInputNodes.length; i++) {
AnimatedNode animatedNode = mNativeAnimatedNodesManager.getNodeById(mInputNodes[i]);
if (animatedNode != null && animatedNode instanceof ValueAnimatedNode) {
double value = ((ValueAnimatedNode) animatedNode).mValue;
if (i == 0) {
mValue = value;
continue;
}
if (value == 0) {
throw new JSApplicationCausedNativeException("Detected a division by zero in " +
"Animated.divide node");
}
mValue /= value;
} else {
throw new JSApplicationCausedNativeException("Illegal node ID set as an input for " +
"Animated.divide node");
}
}
}
}

View File

@ -89,6 +89,8 @@ import javax.annotation.Nullable;
node = new InterpolationAnimatedNode(config);
} else if ("addition".equals(type)) {
node = new AdditionAnimatedNode(config, this);
} else if ("division".equals(type)) {
node = new DivisionAnimatedNode(config, this);
} else if ("multiplication".equals(type)) {
node = new MultiplicationAnimatedNode(config, this);
} else if ("diffclamp".equals(type)) {