-
Notifications
You must be signed in to change notification settings - Fork 24.7k
Android - Animated.event useNativeDriver:true sets incorrect translate position when scrolling interrupted by user #50496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I tried it on ios there is no issue. |
I looked the value using |
I narrowed it down to: |
When I looked into DiffClampAnimatedNode.kt file I noticed |
@react-native-bot why are you ignoring me :( |
I don't have an exact answer for this one. I believe this is related to an issue we had with Animated + transforms which is still unresolved. @cipolleschi do you have more context or a issue number to link here? |
I found couple of issues but couldn't find any workaround. #10174 : Suggest to use |
I don't have more context on this. @kocburak here you mentioned:
What line are you referring to?
It could be but it is unlikely. About: #50496 (comment)
|
In my Reproducer, all my animation logic is as follows: //Buggy code.
const scrollY = useRef(new Animated.Value(0));
const handleScroll = Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY.current } } }],
{ useNativeDriver: true }
);
const minusScrollY = Animated.multiply(scrollY.current, -0.1);
const stickyTranslateY = Animated.diffClamp(scrollY, -headerHeight, 0);
let stickyStyles: StyleProp<ViewStyle> = {
transform: [{ translateY: stickyTranslateY , }]
} This is the logic of 1 scroll. Bug still can be reproduce with this much. When I changed it to: //Working code.
const scrollY = useRef(new Animated.Value(0));
const handleScroll = Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY.current } } }],
{ useNativeDriver: true }
);
const minusScrollY = Animated.multiply(scrollY.current, -0.1);
//const stickyTranslateY = Animated.diffClamp(minusScrollY, -headerHeight, 0); // <- commented out
let stickyStyles: StyleProp<ViewStyle> = {
transform: [{ translateY: minusScrollY }] // <- changed the argument from stickyTranslateY to minusScrollY
} There is no animation bug (notice the comment on //Buggy code.
const scrollY = useRef(new Animated.Value(0));
const handleScroll = Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY.current } } }],
{ useNativeDriver: true }
);
//const minusScrollY = Animated.multiply(scrollY.current, -0.1); // <- commented out
const stickyTranslateY = Animated.diffClamp(scrollY.curent, -headerHeight, 0); // <- changed the argument from minusScrollY to scrollY.curent
let stickyStyles: StyleProp<ViewStyle> = {
transform: [{ translateY: stickyTranslateY }]
} The issue came back. Edit: |
That is how I figured |
@cortinico I saw this #50716 issue and tried #50496 (comment) with I would like to continue to use new Arch. We are using JNI Turbo Module for some performance improvements. When I tried old arch on my app, it didn't like it 😄 (didn't compile at all). |
I am trying to debug this. "DiffClampAnimatedNode.kt" has "prettyPrint" function. How can I enbale it so that I can see when the value changes ? |
I have been seeing the same issue after upgrading to React Native v0.76.8. Is their any workaround for this for the time being? Converting all off the codebase to use reanimated just because of this does not make sense, but it has become a release blocker for us. |
We don't have any update on this currently, but thank to @kocburak for the thorough investigation.
@cortinico do you know how to enable it? @cortinico In general, if you want to debug android, you need to build React Native from source as Android apps use a prebuild version of React Native, so modifying the files in the node_modules folder will not make any visible change in the executed code. |
It's already enabled. It prints on Line 729 in bef5cc1
|
Hi @sarthak-cars24 Unfortunetly I couldn't find a solution. I tried to re do the same animation in reanimated. It worked but then I find out when I import the reanimated package, other pages that uses the safe-area-context package is broken. Whatever I tried the layout of the app is broken the moment I add the reanimated package. Good luck with yours. Hi @cipolleschi, I will try it. This bug looks like a race condition between the layers of architecture. Probably one of the layer does not update properly. At least thats what I think. But I am very new in react native so I don't have my hopes up |
@cipolleschi I failed spectacularly. I added the lines to
It gave the following error:
I read the lines saying "nmake System could not findt it". So I tried added this PATH to System Variables (I am on Windows 10):
Do you have any comment on this ? What am I doing wrong ? |
That's because build from source doesn't work well with your Windows setup. For build form source on Android, we recommend to use either Linux or Mac machines |
I gave up. I upgraded my project to React Native 0.79 and retried react-native-reanimated. The layout issues I had with RN 0.77 is gone. Probably it was related to Androids Edge-To-Edge thing. I am adding yet another dependency. I will also test it with iOS and than ship it. |
Curiosity got the better of me and bought an Mac Mini. I added /*
* 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.JSApplicationCausedNativeException
import com.facebook.react.bridge.ReadableMap
import kotlin.math.max
import kotlin.math.min
internal class DiffClampAnimatedNode(
config: ReadableMap,
private val nativeAnimatedNodesManager: NativeAnimatedNodesManager
) : ValueAnimatedNode() {
private val inputNodeTag: Int
private val minValue: Double
private val maxValue: Double
private var lastValue: Double = 0.0
init {
inputNodeTag = config.getInt("input")
minValue = config.getDouble("min")
maxValue = config.getDouble("max")
nodeValue = lastValue
}
override fun update() {
val value = inputNodeValue
val diff = value - lastValue
lastValue = value
nodeValue = min(max(nodeValue + diff, minValue), maxValue)
System.out.println("TestBurak: " + prettyPrint()); // <- Inserted Log
}
private val inputNodeValue: Double
get() {
val animatedNode = nativeAnimatedNodesManager.getNodeById(inputNodeTag)
if (animatedNode == null || animatedNode !is ValueAnimatedNode) {
throw JSApplicationCausedNativeException(
"Illegal node ID set as an input for Animated.DiffClamp node")
}
return animatedNode.getValue()
}
override fun prettyPrint(): String =
"DiffClampAnimatedNode[$tag]: InputNodeTag: $inputNodeTag min: $minValue " +
"max: $maxValue lastValue: $lastValue super: ${super.prettyPrint()}"
}
And the logs are like this:
When I let go of the scroll, the bug accours. But there is no log that indicates a faulty behaviour. Both So I added to onTouchEvent of ReactScrollView: public boolean onTouchEvent(MotionEvent ev) {
//..bla bla
if (action == MotionEvent.ACTION_UP && mDragging) {
//..bla bla
System.out.println("TestBurak: Hello From ReactScrollView Finger Up");
}
//..bla bla
} And it confirmed there was no |
I commented out the So of cource I added a log in for (Integer tag : tags) {
tagsArray.pushInt(tag);
System.out.println("TestBurak : " + tag);
}
Also added logs to see orginal tags of nodes in while (!nodesQueue.isEmpty()) {
AnimatedNode nextNode = nodesQueue.poll();
try {
// NULLSAFE_FIXME[Nullable Dereference]
nextNode.update();
System.out.println("TestBurak: " + nextNode.prettyPrint()); Resulting logs is this:
So this final |
I forcefully removed the InterpolationAnimatedNode from array using: for (Integer tag : tags) {
tagsArray.pushInt(tag);
System.out.println("TestBurak : " + tag);
break;
} And the bug still accoured. So the problem is due to the unkown node of |
Summary: I noticed a missing debugging text while working on #50496. It was a typo. I though I might as well send a PR. ## Changelog: [Android] [Fixed] - Fixing a typo in InterpolationAnimatedNode for debug text. <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests Pull Request resolved: #51460 Reviewed By: cortinico, shwanton Differential Revision: D75005096 Pulled By: javache fbshipit-source-id: 390452b2b0c6eabfa003b9c95719649c79444d3a
I did more test and I was wrong. I removed all of this part:
and bug still occurred. When I commented out I checked the NativeAnimatedHelper.nativeEventEmitter.addListener(
'onUserDrivenAnimationEnded',
data => {
console.log("TestBurak createAnimatedPropsHook")
node.update();
},
); And this called only once. And again deleting the
This log also triggered once and removing |
Description
I have 2 transform translateY animation. When the useNativeDriver:false, there is no issue except performance. I can see the animation moves couple frames behind from native animation. When the useNativeDriver:true, while moving fast the component jumps around (usually to final position if there were no distruption to moementum scroll)
Steps to reproduce
npx expo run:android --device
while useNativeDriver: trueReact Native Version
0.77.2
Affected Platforms
Runtime - Android
Output of
npx @react-native-community/cli info
Stacktrace or Logs
Reproducer
https://snack.expo.dev/@kocburak/usenativedriverbug
Screenshots and Videos
Screen_Recording_20250404_190621.mp4
The text was updated successfully, but these errors were encountered: