Support for Animated.multiply node
Summary:This change adds native animated support for Animated.multiply nodes. Animated.multiply allows for defining nodes that would output a product of values of the input nodes. **Test Plan** Run JS tests: `npm test Libraries/Animated/src/__tests__/AnimatedNative-test.js` Run java tests: `buck test ReactAndroid/src/test/java/com/facebook/react/animated` Closes https://github.com/facebook/react-native/pull/7071 Differential Revision: D3197663 fb-gh-sync-id: 35f64244a2482c487a81e5e7cd08f3c0e56d9b78 fbshipit-source-id: 35f64244a2482c487a81e5e7cd08f3c0e56d9b78
This commit is contained in:
parent
3e2a3ca466
commit
ec5dfbf8c7
|
@ -995,6 +995,12 @@ class AnimatedMultiplication extends AnimatedWithChildren {
|
||||||
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
|
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__makeNative() {
|
||||||
|
super.__makeNative();
|
||||||
|
this._a.__makeNative();
|
||||||
|
this._b.__makeNative();
|
||||||
|
}
|
||||||
|
|
||||||
__getValue(): number {
|
__getValue(): number {
|
||||||
return this._a.__getValue() * this._b.__getValue();
|
return this._a.__getValue() * this._b.__getValue();
|
||||||
}
|
}
|
||||||
|
@ -1011,6 +1017,14 @@ class AnimatedMultiplication extends AnimatedWithChildren {
|
||||||
__detach(): void {
|
__detach(): void {
|
||||||
this._a.__removeChild(this);
|
this._a.__removeChild(this);
|
||||||
this._b.__removeChild(this);
|
this._b.__removeChild(this);
|
||||||
|
super.__detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
__getNativeConfig(): any {
|
||||||
|
return {
|
||||||
|
type: 'multiplication',
|
||||||
|
input: [this._a.__getNativeTag(), this._b.__getNativeTag()],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,40 @@ describe('Animated', () => {
|
||||||
.toBeCalledWith(additionCall[1].input[1], { type: 'value', value: 2 });
|
.toBeCalledWith(additionCall[1].input[1], { type: 'value', value: 2 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sends a valid graph description for Animated.multiply nodes', () => {
|
||||||
|
var first = new Animated.Value(2);
|
||||||
|
var second = new Animated.Value(1);
|
||||||
|
|
||||||
|
var c = new Animated.View();
|
||||||
|
c.props = {
|
||||||
|
style: {
|
||||||
|
opacity: Animated.multiply(first, second),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
c.componentWillMount();
|
||||||
|
|
||||||
|
Animated.timing(first, {toValue: 5, duration: 1000, useNativeDriver: true}).start();
|
||||||
|
Animated.timing(second, {toValue: -1, duration: 1000, useNativeDriver: true}).start();
|
||||||
|
|
||||||
|
var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
|
||||||
|
expect(nativeAnimatedModule.createAnimatedNode)
|
||||||
|
.toBeCalledWith(jasmine.any(Number), { type: 'multiplication', input: jasmine.any(Array) });
|
||||||
|
var multiplicationCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||||
|
(call) => call[1].type === 'multiplication'
|
||||||
|
);
|
||||||
|
expect(multiplicationCalls.length).toBe(1);
|
||||||
|
var multiplicationCall = multiplicationCalls[0];
|
||||||
|
var multiplicationNodeTag = multiplicationCall[0];
|
||||||
|
var multiplicationConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
|
||||||
|
(call) => call[1] === multiplicationNodeTag
|
||||||
|
);
|
||||||
|
expect(multiplicationConnectionCalls.length).toBe(2);
|
||||||
|
expect(nativeAnimatedModule.createAnimatedNode)
|
||||||
|
.toBeCalledWith(multiplicationCall[1].input[0], { type: 'value', value: 2 });
|
||||||
|
expect(nativeAnimatedModule.createAnimatedNode)
|
||||||
|
.toBeCalledWith(multiplicationCall[1].input[1], { type: 'value', value: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
it('sends a valid timing animation description', () => {
|
it('sends a valid timing animation description', () => {
|
||||||
var anim = new Animated.Value(0);
|
var anim = new Animated.Value(0);
|
||||||
Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start();
|
Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start();
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
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 a product of their
|
||||||
|
* values
|
||||||
|
*/
|
||||||
|
/*package*/ class MultiplicationAnimatedNode extends ValueAnimatedNode {
|
||||||
|
|
||||||
|
private final NativeAnimatedNodesManager mNativeAnimatedNodesManager;
|
||||||
|
private final int[] mInputNodes;
|
||||||
|
|
||||||
|
public MultiplicationAnimatedNode(
|
||||||
|
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() {
|
||||||
|
mValue = 1;
|
||||||
|
for (int i = 0; i < mInputNodes.length; i++) {
|
||||||
|
AnimatedNode animatedNode = mNativeAnimatedNodesManager.getNodeById(mInputNodes[i]);
|
||||||
|
if (animatedNode != null && animatedNode instanceof ValueAnimatedNode) {
|
||||||
|
mValue *= ((ValueAnimatedNode) animatedNode).mValue;
|
||||||
|
} else {
|
||||||
|
throw new JSApplicationCausedNativeException("Illegal node ID set as an input for " +
|
||||||
|
"Animated.multiply node");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,6 +74,8 @@ import javax.annotation.Nullable;
|
||||||
node = new PropsAnimatedNode(config, this);
|
node = new PropsAnimatedNode(config, this);
|
||||||
} else if ("addition".equals(type)) {
|
} else if ("addition".equals(type)) {
|
||||||
node = new AdditionAnimatedNode(config, this);
|
node = new AdditionAnimatedNode(config, this);
|
||||||
|
} else if ("multiplication".equals(type)) {
|
||||||
|
node = new MultiplicationAnimatedNode(config, this);
|
||||||
} else {
|
} else {
|
||||||
throw new JSApplicationIllegalArgumentException("Unsupported node type: " + type);
|
throw new JSApplicationIllegalArgumentException("Unsupported node type: " + type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,4 +341,64 @@ public class NativeAnimatedNodeTraversalTest {
|
||||||
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||||
verifyNoMoreInteractions(mUIImplementationMock);
|
verifyNoMoreInteractions(mUIImplementationMock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiplicationNode() {
|
||||||
|
mNativeAnimatedNodesManager.createAnimatedNode(
|
||||||
|
1,
|
||||||
|
JavaOnlyMap.of("type", "value", "value", 1d));
|
||||||
|
mNativeAnimatedNodesManager.createAnimatedNode(
|
||||||
|
2,
|
||||||
|
JavaOnlyMap.of("type", "value", "value", 5d));
|
||||||
|
|
||||||
|
mNativeAnimatedNodesManager.createAnimatedNode(
|
||||||
|
3,
|
||||||
|
JavaOnlyMap.of("type", "multiplication", "input", JavaOnlyArray.of(1, 2)));
|
||||||
|
|
||||||
|
mNativeAnimatedNodesManager.createAnimatedNode(
|
||||||
|
4,
|
||||||
|
JavaOnlyMap.of("type", "style", "style", JavaOnlyMap.of("translateX", 3)));
|
||||||
|
mNativeAnimatedNodesManager.createAnimatedNode(
|
||||||
|
5,
|
||||||
|
JavaOnlyMap.of("type", "props", "props", JavaOnlyMap.of("style", 4)));
|
||||||
|
mNativeAnimatedNodesManager.connectAnimatedNodes(1, 3);
|
||||||
|
mNativeAnimatedNodesManager.connectAnimatedNodes(2, 3);
|
||||||
|
mNativeAnimatedNodesManager.connectAnimatedNodes(3, 4);
|
||||||
|
mNativeAnimatedNodesManager.connectAnimatedNodes(4, 5);
|
||||||
|
mNativeAnimatedNodesManager.connectAnimatedNodeToView(5, 50);
|
||||||
|
|
||||||
|
Callback animationCallback = mock(Callback.class);
|
||||||
|
JavaOnlyArray frames = JavaOnlyArray.of(0d, 1d);
|
||||||
|
mNativeAnimatedNodesManager.startAnimatingNode(
|
||||||
|
1,
|
||||||
|
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 2d),
|
||||||
|
animationCallback);
|
||||||
|
|
||||||
|
mNativeAnimatedNodesManager.startAnimatingNode(
|
||||||
|
2,
|
||||||
|
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 10d),
|
||||||
|
animationCallback);
|
||||||
|
|
||||||
|
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
|
||||||
|
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
|
||||||
|
|
||||||
|
reset(mUIImplementationMock);
|
||||||
|
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||||
|
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
|
||||||
|
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(5d);
|
||||||
|
|
||||||
|
reset(mUIImplementationMock);
|
||||||
|
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||||
|
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
|
||||||
|
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(5d);
|
||||||
|
|
||||||
|
reset(mUIImplementationMock);
|
||||||
|
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||||
|
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
|
||||||
|
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(20d);
|
||||||
|
|
||||||
|
reset(mUIImplementationMock);
|
||||||
|
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
|
||||||
|
verifyNoMoreInteractions(mUIImplementationMock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue