Files
react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.java
T
Marc Horowitz 515eb0e801 Fix a race condition in the animation module
Summary:
update() is called from the choreographer, so it can be
invoked asynchronously relative to RN.  If it's called while the node
tree is incomplete, this can be called with no parent.  Don't treat an
unparented node as an invariant failure, just skip over it.

Reviewed By: AaaChiuuu

Differential Revision: D6249038

fbshipit-source-id: d22807dff1659bf29a81893ab97d0fe7c19de512
2017-11-06 13:49:46 -08:00

149 lines
4.4 KiB
Java

/**
* Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
*
* <p>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.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import javax.annotation.Nullable;
/**
* Animated node that corresponds to {@code AnimatedInterpolation} from AnimatedImplementation.js.
*
* Currently only a linear interpolation is supported on an input range of an arbitrary size.
*/
/*package*/ class InterpolationAnimatedNode extends ValueAnimatedNode {
public static final String EXTRAPOLATE_TYPE_IDENTITY = "identity";
public static final String EXTRAPOLATE_TYPE_CLAMP = "clamp";
public static final String EXTRAPOLATE_TYPE_EXTEND = "extend";
private static double[] fromDoubleArray(ReadableArray ary) {
double[] res = new double[ary.size()];
for (int i = 0; i < res.length; i++) {
res[i] = ary.getDouble(i);
}
return res;
}
private static double interpolate(
double value,
double inputMin,
double inputMax,
double outputMin,
double outputMax,
String extrapolateLeft,
String extrapolateRight) {
double result = value;
// Extrapolate
if (result < inputMin) {
switch (extrapolateLeft) {
case EXTRAPOLATE_TYPE_IDENTITY:
return result;
case EXTRAPOLATE_TYPE_CLAMP:
result = inputMin;
break;
case EXTRAPOLATE_TYPE_EXTEND:
break;
default:
throw new JSApplicationIllegalArgumentException(
"Invalid extrapolation type " + extrapolateLeft + "for left extrapolation");
}
}
if (result > inputMax) {
switch (extrapolateRight) {
case EXTRAPOLATE_TYPE_IDENTITY:
return result;
case EXTRAPOLATE_TYPE_CLAMP:
result = inputMax;
break;
case EXTRAPOLATE_TYPE_EXTEND:
break;
default:
throw new JSApplicationIllegalArgumentException(
"Invalid extrapolation type " + extrapolateRight + "for right extrapolation");
}
}
return outputMin + (outputMax - outputMin) *
(result - inputMin) / (inputMax - inputMin);
}
/*package*/ static double interpolate(
double value,
double[] inputRange,
double[] outputRange,
String extrapolateLeft,
String extrapolateRight
) {
int rangeIndex = findRangeIndex(value, inputRange);
return interpolate(
value,
inputRange[rangeIndex],
inputRange[rangeIndex + 1],
outputRange[rangeIndex],
outputRange[rangeIndex + 1],
extrapolateLeft,
extrapolateRight);
}
private static int findRangeIndex(double value, double[] ranges) {
int index;
for (index = 1; index < ranges.length - 1; index++) {
if (ranges[index] >= value) {
break;
}
}
return index - 1;
}
private final double mInputRange[];
private final double mOutputRange[];
private final String mExtrapolateLeft;
private final String mExtrapolateRight;
private @Nullable ValueAnimatedNode mParent;
public InterpolationAnimatedNode(ReadableMap config) {
mInputRange = fromDoubleArray(config.getArray("inputRange"));
mOutputRange = fromDoubleArray(config.getArray("outputRange"));
mExtrapolateLeft = config.getString("extrapolateLeft");
mExtrapolateRight = config.getString("extrapolateRight");
}
@Override
public void onAttachedToNode(AnimatedNode parent) {
if (mParent != null) {
throw new IllegalStateException("Parent already attached");
}
if (!(parent instanceof ValueAnimatedNode)) {
throw new IllegalArgumentException("Parent is of an invalid type");
}
mParent = (ValueAnimatedNode) parent;
}
@Override
public void onDetachedFromNode(AnimatedNode parent) {
if (parent != mParent) {
throw new IllegalArgumentException("Invalid parent node provided");
}
mParent = null;
}
@Override
public void update() {
if (mParent == null) {
// The graph is in the middle of being created, just skip this
// unattached node.
return;
}
mValue = interpolate(mParent.getValue(), mInputRange, mOutputRange, mExtrapolateLeft, mExtrapolateRight);
}
}