diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.java deleted file mode 100644 index 97463913dae684..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.java +++ /dev/null @@ -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.mValue; - } else { // not the first iteration, reset mAnimatedValue based on mFromValue - mAnimatedValue.mValue = mFromValue; - } - mLastValue = mAnimatedValue.mValue; - } - - 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.mValue = value; - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.kt new file mode 100644 index 00000000000000..86a91103660cbc --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DecayAnimation.kt @@ -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.mValue + } else { // not the first iteration, reset mAnimatedValue based on mFromValue + mAnimatedValue.mValue = fromValue + } + lastValue = mAnimatedValue.mValue + } + 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.mValue = value + } +}