mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Kotlinify AnimationDriver (#45836)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45836 # Changelog: [Internal] - As in the title. Reviewed By: cortinico Differential Revision: D60502487 fbshipit-source-id: 003f2eade8f8425636b2023397ec29545175b487
This commit is contained in:
committed by
Facebook GitHub Bot
parent
fdfa0b17bb
commit
c30ee464eb
+13
-14
@@ -5,28 +5,27 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.animated;
|
||||
package com.facebook.react.animated
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.JSApplicationCausedNativeException;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.Callback
|
||||
import com.facebook.react.bridge.JSApplicationCausedNativeException
|
||||
import com.facebook.react.bridge.ReadableMap
|
||||
|
||||
/**
|
||||
* Base class for different types of animation drivers. Can be used to implement simple time-based
|
||||
* animations as well as spring based animations.
|
||||
*/
|
||||
/*package*/ abstract class AnimationDriver {
|
||||
|
||||
/*package*/ boolean mHasFinished = false;
|
||||
/*package*/ ValueAnimatedNode mAnimatedValue;
|
||||
/*package*/ Callback mEndCallback;
|
||||
/*package*/ int mId;
|
||||
internal abstract class AnimationDriver {
|
||||
@JvmField internal var hasFinished = false
|
||||
@JvmField internal var animatedValue: ValueAnimatedNode? = null
|
||||
@JvmField internal var endCallback: Callback? = null
|
||||
@JvmField internal var id = 0
|
||||
|
||||
/**
|
||||
* This method gets called in the main animation loop with a frame time passed down from the
|
||||
* android choreographer callback.
|
||||
*/
|
||||
public abstract void runAnimationStep(long frameTimeNanos);
|
||||
abstract fun runAnimationStep(frameTimeNanos: Long)
|
||||
|
||||
/**
|
||||
* This method will get called when some of the configuration gets updated while the animation is
|
||||
@@ -34,8 +33,8 @@ import com.facebook.react.bridge.ReadableMap;
|
||||
* transition. E.g. in case of a spring animation we want to keep the current value and speed and
|
||||
* start animating with the new properties (different destination or spring settings)
|
||||
*/
|
||||
public void resetConfig(ReadableMap config) {
|
||||
throw new JSApplicationCausedNativeException(
|
||||
"Animation config for " + getClass().getSimpleName() + " cannot be reset");
|
||||
open fun resetConfig(config: ReadableMap) {
|
||||
throw JSApplicationCausedNativeException(
|
||||
"Animation config for ${javaClass.simpleName} cannot be reset")
|
||||
}
|
||||
}
|
||||
+11
-9
@@ -36,20 +36,21 @@ internal class DecayAnimation(config: ReadableMap) : AnimationDriver() {
|
||||
lastValue = 0.0
|
||||
iterations = if (config.hasKey("iterations")) config.getInt("iterations") else 1
|
||||
currentLoop = 1
|
||||
mHasFinished = iterations == 0
|
||||
hasFinished = iterations == 0
|
||||
}
|
||||
|
||||
public override fun runAnimationStep(frameTimeNanos: Long) {
|
||||
val animatedValue = requireNotNull(animatedValue) { "Animated value should not be null" }
|
||||
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
|
||||
if (fromValue == lastValue) { // first iteration, assign [fromValue] based on [animatedValue]
|
||||
fromValue = animatedValue.nodeValue
|
||||
} else { // not the first iteration, reset [animatedValue] based on [fromValue]
|
||||
animatedValue.nodeValue = fromValue
|
||||
}
|
||||
lastValue = mAnimatedValue.nodeValue
|
||||
lastValue = animatedValue.nodeValue
|
||||
}
|
||||
val value =
|
||||
(fromValue +
|
||||
@@ -57,15 +58,16 @@ internal class DecayAnimation(config: ReadableMap) : AnimationDriver() {
|
||||
(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
|
||||
// set [startFrameTimeMillis] to -1 to reset instance variables on the next
|
||||
// [runAnimationStep]
|
||||
startFrameTimeMillis = -1
|
||||
currentLoop++
|
||||
} else { // animation has completed
|
||||
mHasFinished = true
|
||||
hasFinished = true
|
||||
return
|
||||
}
|
||||
}
|
||||
lastValue = value
|
||||
mAnimatedValue.nodeValue = value
|
||||
animatedValue.nodeValue = value
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -48,16 +48,17 @@ internal class FrameBasedAnimationDriver(config: ReadableMap) : AnimationDriver(
|
||||
config.getInt("iterations")
|
||||
else 1
|
||||
currentLoop = 1
|
||||
mHasFinished = iterations == 0
|
||||
hasFinished = iterations == 0
|
||||
startFrameTimeNanos = -1
|
||||
}
|
||||
|
||||
override fun runAnimationStep(frameTimeNanos: Long) {
|
||||
val animatedValue = requireNotNull(animatedValue) { "Animated value should not be null" }
|
||||
if (startFrameTimeNanos < 0) {
|
||||
startFrameTimeNanos = frameTimeNanos
|
||||
if (currentLoop == 1) {
|
||||
// initiate start value when animation runs for the first time
|
||||
fromValue = mAnimatedValue.nodeValue
|
||||
fromValue = animatedValue.nodeValue
|
||||
}
|
||||
}
|
||||
val timeFromStartMillis = (frameTimeNanos - startFrameTimeNanos) / 1000000
|
||||
@@ -74,7 +75,7 @@ internal class FrameBasedAnimationDriver(config: ReadableMap) : AnimationDriver(
|
||||
logCount++
|
||||
}
|
||||
return
|
||||
} else if (mHasFinished) {
|
||||
} else if (hasFinished) {
|
||||
// nothing to do here
|
||||
return
|
||||
}
|
||||
@@ -88,12 +89,12 @@ internal class FrameBasedAnimationDriver(config: ReadableMap) : AnimationDriver(
|
||||
} else {
|
||||
// animation has completed, no more frames left
|
||||
nextValue = toValue
|
||||
mHasFinished = true
|
||||
hasFinished = true
|
||||
}
|
||||
} else {
|
||||
nextValue = fromValue + frames[frameIndex] * (toValue - fromValue)
|
||||
}
|
||||
mAnimatedValue.nodeValue = nextValue
|
||||
animatedValue.nodeValue = nextValue
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+23
-23
@@ -286,9 +286,9 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"startAnimatingNode: Unsupported animation type [" + animatedNodeTag + "]: " + type);
|
||||
}
|
||||
animation.mId = animationId;
|
||||
animation.mEndCallback = endCallback;
|
||||
animation.mAnimatedValue = (ValueAnimatedNode) node;
|
||||
animation.id = animationId;
|
||||
animation.endCallback = endCallback;
|
||||
animation.animatedValue = (ValueAnimatedNode) node;
|
||||
mActiveAnimations.put(animationId, animation);
|
||||
}
|
||||
|
||||
@@ -301,21 +301,21 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
|
||||
WritableArray events = null;
|
||||
for (int i = 0; i < mActiveAnimations.size(); i++) {
|
||||
AnimationDriver animation = mActiveAnimations.valueAt(i);
|
||||
if (animatedNode.equals(animation.mAnimatedValue)) {
|
||||
if (animation.mEndCallback != null) {
|
||||
if (animatedNode.equals(animation.animatedValue)) {
|
||||
if (animation.endCallback != null) {
|
||||
// Invoke animation end callback with {finished: false}
|
||||
WritableMap endCallbackResponse = Arguments.createMap();
|
||||
endCallbackResponse.putBoolean("finished", false);
|
||||
endCallbackResponse.putDouble("value", animation.mAnimatedValue.nodeValue);
|
||||
animation.mEndCallback.invoke(endCallbackResponse);
|
||||
endCallbackResponse.putDouble("value", animation.animatedValue.nodeValue);
|
||||
animation.endCallback.invoke(endCallbackResponse);
|
||||
} else if (mReactApplicationContext != null) {
|
||||
// If no callback is passed in, this /may/ be an animation set up by the single-op
|
||||
// instruction from JS, meaning that no jsi::functions are passed into native and
|
||||
// we communicate via RCTDeviceEventEmitter instead of callbacks.
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putInt("animationId", animation.mId);
|
||||
params.putInt("animationId", animation.id);
|
||||
params.putBoolean("finished", false);
|
||||
params.putDouble("value", animation.mAnimatedValue.nodeValue);
|
||||
params.putDouble("value", animation.animatedValue.nodeValue);
|
||||
if (events == null) {
|
||||
events = Arguments.createArray();
|
||||
}
|
||||
@@ -339,21 +339,21 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
|
||||
WritableArray events = null;
|
||||
for (int i = 0; i < mActiveAnimations.size(); i++) {
|
||||
AnimationDriver animation = mActiveAnimations.valueAt(i);
|
||||
if (animation.mId == animationId) {
|
||||
if (animation.mEndCallback != null) {
|
||||
if (animation.id == animationId) {
|
||||
if (animation.endCallback != null) {
|
||||
// Invoke animation end callback with {finished: false}
|
||||
WritableMap endCallbackResponse = Arguments.createMap();
|
||||
endCallbackResponse.putBoolean("finished", false);
|
||||
endCallbackResponse.putDouble("value", animation.mAnimatedValue.nodeValue);
|
||||
animation.mEndCallback.invoke(endCallbackResponse);
|
||||
endCallbackResponse.putDouble("value", animation.animatedValue.nodeValue);
|
||||
animation.endCallback.invoke(endCallbackResponse);
|
||||
} else if (mReactApplicationContext != null) {
|
||||
// If no callback is passed in, this /may/ be an animation set up by the single-op
|
||||
// instruction from JS, meaning that no jsi::functions are passed into native and
|
||||
// we communicate via RCTDeviceEventEmitter instead of callbacks.
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putInt("animationId", animation.mId);
|
||||
params.putInt("animationId", animation.id);
|
||||
params.putBoolean("finished", false);
|
||||
params.putDouble("value", animation.mAnimatedValue.nodeValue);
|
||||
params.putDouble("value", animation.animatedValue.nodeValue);
|
||||
if (events == null) {
|
||||
events = Arguments.createArray();
|
||||
}
|
||||
@@ -652,9 +652,9 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
|
||||
for (int i = 0; i < mActiveAnimations.size(); i++) {
|
||||
AnimationDriver animation = mActiveAnimations.valueAt(i);
|
||||
animation.runAnimationStep(frameTimeNanos);
|
||||
AnimatedNode valueNode = animation.mAnimatedValue;
|
||||
AnimatedNode valueNode = animation.animatedValue;
|
||||
mRunUpdateNodeList.add(valueNode);
|
||||
if (animation.mHasFinished) {
|
||||
if (animation.hasFinished) {
|
||||
hasFinishedAnimations = true;
|
||||
}
|
||||
}
|
||||
@@ -668,20 +668,20 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener {
|
||||
WritableArray events = null;
|
||||
for (int i = mActiveAnimations.size() - 1; i >= 0; i--) {
|
||||
AnimationDriver animation = mActiveAnimations.valueAt(i);
|
||||
if (animation.mHasFinished) {
|
||||
if (animation.mEndCallback != null) {
|
||||
if (animation.hasFinished) {
|
||||
if (animation.endCallback != null) {
|
||||
WritableMap endCallbackResponse = Arguments.createMap();
|
||||
endCallbackResponse.putBoolean("finished", true);
|
||||
endCallbackResponse.putDouble("value", animation.mAnimatedValue.nodeValue);
|
||||
animation.mEndCallback.invoke(endCallbackResponse);
|
||||
endCallbackResponse.putDouble("value", animation.animatedValue.nodeValue);
|
||||
animation.endCallback.invoke(endCallbackResponse);
|
||||
} else if (mReactApplicationContext != null) {
|
||||
// If no callback is passed in, this /may/ be an animation set up by the single-op
|
||||
// instruction from JS, meaning that no jsi::functions are passed into native and
|
||||
// we communicate via RCTDeviceEventEmitter instead of callbacks.
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putInt("animationId", animation.mId);
|
||||
params.putInt("animationId", animation.id);
|
||||
params.putBoolean("finished", true);
|
||||
params.putDouble("value", animation.mAnimatedValue.nodeValue);
|
||||
params.putDouble("value", animation.animatedValue.nodeValue);
|
||||
if (events == null) {
|
||||
events = Arguments.createArray();
|
||||
}
|
||||
|
||||
+7
-6
@@ -55,20 +55,21 @@ internal class SpringAnimation(config: ReadableMap) : AnimationDriver() {
|
||||
displacementFromRestThreshold = config.getDouble("restDisplacementThreshold")
|
||||
overshootClampingEnabled = config.getBoolean("overshootClamping")
|
||||
iterations = if (config.hasKey("iterations")) config.getInt("iterations") else 1
|
||||
mHasFinished = iterations == 0
|
||||
hasFinished = iterations == 0
|
||||
currentLoop = 0
|
||||
timeAccumulator = 0.0
|
||||
springStarted = false
|
||||
}
|
||||
|
||||
override fun runAnimationStep(frameTimeNanos: Long) {
|
||||
val animatedValue = requireNotNull(animatedValue) { "Animated value should not be null" }
|
||||
val frameTimeMillis = frameTimeNanos / 1_000_000
|
||||
if (!springStarted) {
|
||||
if (currentLoop == 0) {
|
||||
originalValue = mAnimatedValue.nodeValue
|
||||
originalValue = animatedValue.nodeValue
|
||||
currentLoop = 1
|
||||
}
|
||||
currentState.position = mAnimatedValue.nodeValue
|
||||
currentState.position = animatedValue.nodeValue
|
||||
startValue = currentState.position
|
||||
lastTime = frameTimeMillis
|
||||
timeAccumulator = 0.0
|
||||
@@ -76,14 +77,14 @@ internal class SpringAnimation(config: ReadableMap) : AnimationDriver() {
|
||||
}
|
||||
advance((frameTimeMillis - lastTime) / 1000.0)
|
||||
lastTime = frameTimeMillis
|
||||
mAnimatedValue.nodeValue = currentState.position
|
||||
animatedValue.nodeValue = currentState.position
|
||||
if (isAtRest) {
|
||||
if (iterations == -1 || currentLoop < iterations) { // looping animation, return to start
|
||||
springStarted = false
|
||||
mAnimatedValue.nodeValue = originalValue
|
||||
animatedValue.nodeValue = originalValue
|
||||
currentLoop++
|
||||
} else { // animation has completed
|
||||
mHasFinished = true
|
||||
hasFinished = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user