mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Fix connection of animated nodes and scroll offset with useNativeDriver. (#24177)
Summary: Add example showing regression before this fix is applied. https://github.com/facebook/react-native/pull/18187 Was found to introduce a regression in some internal facebook code-base end to end test which couldn't be shared. I was able to create a reproducible demo of a regression I found, and made a fix for it. Hopefully this will fix the internal test, such that the pr can stay merged. ## Changelog [GENERAL] [Fixed] - Fix connection of animated nodes and scroll offset with useNativeDriver. Pull Request resolved: https://github.com/facebook/react-native/pull/24177 Reviewed By: rickhanlonii Differential Revision: D14845617 Pulled By: cpojer fbshipit-source-id: 1f121dbe773b0cde2adf1ee5a8c3c0266034e50d
This commit is contained in:
committed by
Facebook Github Bot
parent
417e191a1c
commit
bdc530b9bb
+96
-2
@@ -9,6 +9,12 @@ package com.facebook.react.animated;
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@@ -22,6 +28,9 @@ import javax.annotation.Nullable;
|
||||
public static final String EXTRAPOLATE_TYPE_CLAMP = "clamp";
|
||||
public static final String EXTRAPOLATE_TYPE_EXTEND = "extend";
|
||||
|
||||
private static final String fpRegex = "[+-]?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?";
|
||||
private static final Pattern fpPattern = Pattern.compile(fpRegex);
|
||||
|
||||
private static double[] fromDoubleArray(ReadableArray ary) {
|
||||
double[] res = new double[ary.size()];
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
@@ -116,13 +125,68 @@ import javax.annotation.Nullable;
|
||||
|
||||
private final double mInputRange[];
|
||||
private final double mOutputRange[];
|
||||
private String mPattern;
|
||||
private double mOutputs[][];
|
||||
private final boolean mHasStringOutput;
|
||||
private final Matcher mSOutputMatcher;
|
||||
private final String mExtrapolateLeft;
|
||||
private final String mExtrapolateRight;
|
||||
private @Nullable ValueAnimatedNode mParent;
|
||||
private boolean mShouldRound;
|
||||
private int mNumVals;
|
||||
|
||||
public InterpolationAnimatedNode(ReadableMap config) {
|
||||
mInputRange = fromDoubleArray(config.getArray("inputRange"));
|
||||
mOutputRange = fromDoubleArray(config.getArray("outputRange"));
|
||||
ReadableArray output = config.getArray("outputRange");
|
||||
mHasStringOutput = output.getType(0) == ReadableType.String;
|
||||
if (mHasStringOutput) {
|
||||
/*
|
||||
* Supports string shapes by extracting numbers so new values can be computed,
|
||||
* and recombines those values into new strings of the same shape. Supports
|
||||
* things like:
|
||||
*
|
||||
* rgba(123, 42, 99, 0.36) // colors
|
||||
* -45deg // values with units
|
||||
*/
|
||||
int size = output.size();
|
||||
mOutputRange = new double[size];
|
||||
mPattern = output.getString(0);
|
||||
mShouldRound = mPattern.startsWith("rgb");
|
||||
mSOutputMatcher = fpPattern.matcher(mPattern);
|
||||
ArrayList<ArrayList<Double>> mOutputRanges = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
String val = output.getString(i);
|
||||
Matcher m = fpPattern.matcher(val);
|
||||
ArrayList<Double> outputRange = new ArrayList<>();
|
||||
mOutputRanges.add(outputRange);
|
||||
while (m.find()) {
|
||||
Double parsed = Double.parseDouble(m.group());
|
||||
outputRange.add(parsed);
|
||||
}
|
||||
mOutputRange[i] = outputRange.get(0);
|
||||
}
|
||||
|
||||
// ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.5)']
|
||||
// ->
|
||||
// [
|
||||
// [0, 50],
|
||||
// [100, 150],
|
||||
// [200, 250],
|
||||
// [0, 0.5],
|
||||
// ]
|
||||
mNumVals = mOutputRanges.get(0).size();
|
||||
mOutputs = new double[mNumVals][];
|
||||
for (int j = 0; j < mNumVals; j++) {
|
||||
double[] arr = new double[size];
|
||||
mOutputs[j] = arr;
|
||||
for (int i = 0; i < size; i++) {
|
||||
arr[i] = mOutputRanges.get(i).get(j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mOutputRange = fromDoubleArray(output);
|
||||
mSOutputMatcher = null;
|
||||
}
|
||||
mExtrapolateLeft = config.getString("extrapolateLeft");
|
||||
mExtrapolateRight = config.getString("extrapolateRight");
|
||||
}
|
||||
@@ -153,6 +217,36 @@ import javax.annotation.Nullable;
|
||||
// unattached node.
|
||||
return;
|
||||
}
|
||||
mValue = interpolate(mParent.getValue(), mInputRange, mOutputRange, mExtrapolateLeft, mExtrapolateRight);
|
||||
double value = mParent.getValue();
|
||||
mValue = interpolate(value, mInputRange, mOutputRange, mExtrapolateLeft, mExtrapolateRight);
|
||||
if (mHasStringOutput) {
|
||||
// 'rgba(0, 100, 200, 0)'
|
||||
// ->
|
||||
// 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...'
|
||||
if (mNumVals > 1) {
|
||||
StringBuffer sb = new StringBuffer(mPattern.length());
|
||||
int i = 0;
|
||||
mSOutputMatcher.reset();
|
||||
while (mSOutputMatcher.find()) {
|
||||
double val = interpolate(value, mInputRange, mOutputs[i++], mExtrapolateLeft, mExtrapolateRight);
|
||||
if (mShouldRound) {
|
||||
// rgba requires that the r,g,b are integers.... so we want to round them, but we *dont* want to
|
||||
// round the opacity (4th column).
|
||||
boolean isAlpha = i == 4;
|
||||
int rounded = (int)Math.round(isAlpha ? val * 1000 : val);
|
||||
String num = isAlpha ? Double.toString((double)rounded / 1000) : Integer.toString(rounded);
|
||||
mSOutputMatcher.appendReplacement(sb, num);
|
||||
} else {
|
||||
int intVal = (int)val;
|
||||
String num = intVal != val ? Double.toString(val) : Integer.toString(intVal);
|
||||
mSOutputMatcher.appendReplacement(sb, num);
|
||||
}
|
||||
}
|
||||
mSOutputMatcher.appendTail(sb);
|
||||
mAnimatedObject = sb.toString();
|
||||
} else {
|
||||
mAnimatedObject = mSOutputMatcher.replaceFirst(String.valueOf(mValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,12 @@ import javax.annotation.Nullable;
|
||||
} else if (node instanceof StyleAnimatedNode) {
|
||||
((StyleAnimatedNode) node).collectViewUpdates(mPropMap);
|
||||
} else if (node instanceof ValueAnimatedNode) {
|
||||
mPropMap.putDouble(entry.getKey(), ((ValueAnimatedNode) node).getValue());
|
||||
Object animatedObject = ((ValueAnimatedNode) node).getAnimatedObject();
|
||||
if (animatedObject instanceof String) {
|
||||
mPropMap.putString(entry.getKey(), (String)animatedObject);
|
||||
} else {
|
||||
mPropMap.putDouble(entry.getKey(), ((ValueAnimatedNode) node).getValue());
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported type of node used in property node " +
|
||||
node.getClass());
|
||||
|
||||
@@ -16,6 +16,7 @@ import javax.annotation.Nullable;
|
||||
* library.
|
||||
*/
|
||||
/*package*/ class ValueAnimatedNode extends AnimatedNode {
|
||||
/*package*/ Object mAnimatedObject = null;
|
||||
/*package*/ double mValue = Double.NaN;
|
||||
/*package*/ double mOffset = 0;
|
||||
private @Nullable AnimatedNodeValueListener mValueListener;
|
||||
@@ -33,6 +34,10 @@ import javax.annotation.Nullable;
|
||||
return mOffset + mValue;
|
||||
}
|
||||
|
||||
public Object getAnimatedObject() {
|
||||
return mAnimatedObject;
|
||||
}
|
||||
|
||||
public void flattenOffset() {
|
||||
mValue += mOffset;
|
||||
mOffset = 0;
|
||||
|
||||
Reference in New Issue
Block a user