Support for spring animations

Summary:
This change adds support for spring animations to be run off the JS thread on android. The implementation is based on the android spring implementation from Rebound (http://facebook.github.io/rebound/) but since only a small subset of the library is used the relevant parts are copied instead of making RN to import the whole library.

**Test Plan**
Run java tests: `buck test ReactAndroid/src/test/java/com/facebook/react/animated`
Add `useNativeDriver: true` to spring animation in animated example app, run it on android
Closes https://github.com/facebook/react-native/pull/8860

Differential Revision: D3676436

fbshipit-source-id: 3a4b1b006725a938562712989b93dd4090577c48
This commit is contained in:
Krzysztof Magiera
2016-08-05 12:01:49 -07:00
committed by Facebook Github Bot 2
parent 0222107170
commit 8f75d7346f
4 changed files with 302 additions and 1 deletions

View File

@@ -35,6 +35,7 @@ import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -198,6 +199,66 @@ public class NativeAnimatedNodeTraversalTest {
verifyNoMoreInteractions(valueListener);
}
@Test
public void testSpringAnimation() {
createSimpleAnimatedViewWithOpacity(1000, 0d);
Callback animationCallback = mock(Callback.class);
mNativeAnimatedNodesManager.startAnimatingNode(
1,
1,
JavaOnlyMap.of(
"type",
"spring",
"friction",
7d,
"tension",
40.0d,
"initialVelocity",
0d,
"toValue",
1d,
"restSpeedThreshold",
0.001d,
"restDisplacementThreshold",
0.001d,
"overshootClamping",
false),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
reset(mUIImplementationMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0);
double previousValue = 0d;
boolean wasGreaterThanOne = false;
/* run 3 secs of animation */
for (int i = 0; i < 3 * 60; i++) {
reset(mUIImplementationMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock, atMost(1))
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
double currentValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN);
if (currentValue > 1d) {
wasGreaterThanOne = true;
}
// verify that animation step is relatively small
assertThat(Math.abs(currentValue - previousValue)).isLessThan(0.1d);
previousValue = currentValue;
}
// verify that we've reach the final value at the end of animation
assertThat(previousValue).isEqualTo(1d);
// verify that value has reached some maximum value that is greater than the final value (bounce)
assertThat(wasGreaterThanOne);
reset(mUIImplementationMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
}
@Test
public void testAnimationCallbackFinish() {
createSimpleAnimatedViewWithOpacity(1000, 0d);