Permalink
Browse files

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
  • Loading branch information...
1 parent 9af620b commit 0a0dd30c6a3761bb7e48637a1a9cbb47d277f7f2 @fkgozali fkgozali committed with Facebook Github Bot 6 Sep 26, 2016
@@ -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.
@@ -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
@@ -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
@@ -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 */,
@@ -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 @@ - (dispatch_queue_t)methodQueue
@"interpolation" : [RCTInterpolationAnimatedNode class],
@"addition" : [RCTAdditionAnimatedNode class],
@"diffclamp": [RCTDiffClampAnimatedNode class],
+ @"division" : [RCTDivisionAnimatedNode class],
@"multiplication" : [RCTMultiplicationAnimatedNode class],
@"modulus" : [RCTModuloAnimatedNode class],
@"transform" : [RCTTransformAnimatedNode class]};
@@ -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");
+ }
+ }
+ }
+}
@@ -89,6 +89,8 @@ public void createAnimatedNode(int tag, ReadableMap config) {
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)) {

0 comments on commit 0a0dd30

Please sign in to comment.