mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Migrate DecayAnimation to Kotlin (#45719)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45719 # Changelog: [Internal] - As in the title. Reviewed By: tdn120 Differential Revision: D60284323 fbshipit-source-id: 6cf47a9f9528067b397b0c52b763b416bf62ed9f
This commit is contained in:
committed by
Facebook GitHub Bot
parent
e86a5ee274
commit
9e7760210f
-79
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.animated;
|
||||
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AnimationDriver} providing support for decay animations. The
|
||||
* implementation is copied from the JS version in {@code AnimatedImplementation.js}.
|
||||
*/
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
class DecayAnimation extends AnimationDriver {
|
||||
|
||||
private final double mVelocity;
|
||||
|
||||
private double mDeceleration;
|
||||
private long mStartFrameTimeMillis;
|
||||
private double mFromValue;
|
||||
private double mLastValue;
|
||||
private int mIterations;
|
||||
private int mCurrentLoop;
|
||||
|
||||
public DecayAnimation(ReadableMap config) {
|
||||
mVelocity = config.getDouble("velocity"); // initial velocity
|
||||
resetConfig(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConfig(ReadableMap config) {
|
||||
mDeceleration = config.getDouble("deceleration");
|
||||
mIterations = config.hasKey("iterations") ? config.getInt("iterations") : 1;
|
||||
mCurrentLoop = 1;
|
||||
mHasFinished = mIterations == 0;
|
||||
mStartFrameTimeMillis = -1;
|
||||
mFromValue = 0;
|
||||
mLastValue = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runAnimationStep(long frameTimeNanos) {
|
||||
long frameTimeMillis = frameTimeNanos / 1000000;
|
||||
if (mStartFrameTimeMillis == -1) {
|
||||
// since this is the first animation step, consider the start to be on the previous frame
|
||||
mStartFrameTimeMillis = frameTimeMillis - 16;
|
||||
if (mFromValue == mLastValue) { // first iteration, assign mFromValue based on mAnimatedValue
|
||||
mFromValue = mAnimatedValue.nodeValue;
|
||||
} else { // not the first iteration, reset mAnimatedValue based on mFromValue
|
||||
mAnimatedValue.nodeValue = mFromValue;
|
||||
}
|
||||
mLastValue = mAnimatedValue.nodeValue;
|
||||
}
|
||||
|
||||
final double value =
|
||||
mFromValue
|
||||
+ (mVelocity / (1 - mDeceleration))
|
||||
* (1 - Math.exp(-(1 - mDeceleration) * (frameTimeMillis - mStartFrameTimeMillis)));
|
||||
|
||||
if (Math.abs(mLastValue - value) < 0.1) {
|
||||
|
||||
if (mIterations == -1 || mCurrentLoop < mIterations) { // looping animation, return to start
|
||||
// set mStartFrameTimeMillis to -1 to reset instance variables on the next runAnimationStep
|
||||
mStartFrameTimeMillis = -1;
|
||||
mCurrentLoop++;
|
||||
} else { // animation has completed
|
||||
mHasFinished = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mLastValue = value;
|
||||
mAnimatedValue.nodeValue = value;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.animated
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.exp
|
||||
|
||||
/**
|
||||
* Implementation of [AnimationDriver] providing support for decay animations. The implementation is
|
||||
* copied from the JS version in `AnimatedImplementation.js`.
|
||||
*/
|
||||
internal class DecayAnimation(config: ReadableMap) : AnimationDriver() {
|
||||
private var velocity: Double = 0.0
|
||||
private var deceleration: Double = 0.0
|
||||
private var startFrameTimeMillis: Long = -1
|
||||
private var fromValue: Double = 0.0
|
||||
private var lastValue: Double = 0.0
|
||||
private var iterations: Int = 1
|
||||
private var currentLoop: Int = 1
|
||||
|
||||
init {
|
||||
resetConfig(config)
|
||||
}
|
||||
|
||||
public override fun resetConfig(config: ReadableMap): Unit {
|
||||
velocity = config.getDouble("velocity")
|
||||
deceleration = config.getDouble("deceleration")
|
||||
startFrameTimeMillis = -1
|
||||
fromValue = 0.0
|
||||
lastValue = 0.0
|
||||
iterations = if (config.hasKey("iterations")) config.getInt("iterations") else 1
|
||||
currentLoop = 1
|
||||
mHasFinished = iterations == 0
|
||||
}
|
||||
|
||||
public override fun runAnimationStep(frameTimeNanos: Long) {
|
||||
val frameTimeMillis = frameTimeNanos / 1000000
|
||||
if (startFrameTimeMillis == -1L) {
|
||||
// since this is the first animation step, consider the start to be on the previous frame
|
||||
startFrameTimeMillis = frameTimeMillis - 16
|
||||
if (fromValue == lastValue) { // first iteration, assign mFromValue based on mAnimatedValue
|
||||
fromValue = mAnimatedValue.nodeValue
|
||||
} else { // not the first iteration, reset mAnimatedValue based on mFromValue
|
||||
mAnimatedValue.nodeValue = fromValue
|
||||
}
|
||||
lastValue = mAnimatedValue.nodeValue
|
||||
}
|
||||
val value =
|
||||
(fromValue +
|
||||
velocity / (1 - deceleration) *
|
||||
(1 - exp(-(1 - deceleration) * (frameTimeMillis - startFrameTimeMillis))))
|
||||
if (abs(lastValue - value) < 0.1) {
|
||||
if (iterations == -1 || currentLoop < iterations) { // looping animation, return to start
|
||||
// set mStartFrameTimeMillis to -1 to reset instance variables on the next runAnimationStep
|
||||
startFrameTimeMillis = -1
|
||||
currentLoop++
|
||||
} else { // animation has completed
|
||||
mHasFinished = true
|
||||
return
|
||||
}
|
||||
}
|
||||
lastValue = value
|
||||
mAnimatedValue.nodeValue = value
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1120,7 +1120,7 @@ class NativeAnimatedNodeTraversalTest {
|
||||
}
|
||||
|
||||
// at this point we expect tracking value to be at 75
|
||||
assertThat((nativeAnimatedNodesManager.getNodeById(3) as ValueAnimatedNode).value)
|
||||
assertThat((nativeAnimatedNodesManager.getNodeById(3) as ValueAnimatedNode).getValue())
|
||||
.isEqualTo(75.0)
|
||||
|
||||
// we update "toValue" again to 100 and expect the animation to restart from the current
|
||||
@@ -1228,11 +1228,11 @@ class NativeAnimatedNodeTraversalTest {
|
||||
// passes the final point (that is 1) while going backwards
|
||||
var isBoucingBack: Boolean = false
|
||||
var previousValue: Double =
|
||||
(nativeAnimatedNodesManager.getNodeById(3) as ValueAnimatedNode).value
|
||||
(nativeAnimatedNodesManager.getNodeById(3) as ValueAnimatedNode).getValue()
|
||||
for (i in 500 downTo 0) {
|
||||
nativeAnimatedNodesManager.runUpdates(nextFrameTime())
|
||||
val currentValue: Double =
|
||||
(nativeAnimatedNodesManager.getNodeById(3) as ValueAnimatedNode).value
|
||||
(nativeAnimatedNodesManager.getNodeById(3) as ValueAnimatedNode).getValue()
|
||||
if (previousValue >= 1.0 && currentValue < 1.0) {
|
||||
isBoucingBack = true
|
||||
break
|
||||
@@ -1253,7 +1253,7 @@ class NativeAnimatedNodeTraversalTest {
|
||||
for (i in 0 until 8 * 60) {
|
||||
nativeAnimatedNodesManager.runUpdates(nextFrameTime())
|
||||
val currentValue: Double =
|
||||
(nativeAnimatedNodesManager.getNodeById(3) as ValueAnimatedNode).value
|
||||
(nativeAnimatedNodesManager.getNodeById(3) as ValueAnimatedNode).getValue()
|
||||
if (!hasTurnedForward) {
|
||||
if (currentValue <= previousValue) {
|
||||
bounceBackInitialFrames++
|
||||
|
||||
Reference in New Issue
Block a user