software-mansion / react-native-reanimated

React Native's Animated library reimplemented
https://docs.swmansion.com/react-native-reanimated/
MIT License
8.6k stars 1.26k forks source link

`useFrameCallback` speeds up considerably when adding a Gesture #6189

Closed friyiajr closed 1 day ago

friyiajr commented 3 days ago

Description

I am working on creating a small game where items can collide with a ball. When I was trying to add a gesture I realized that even if the gesture wasn't changing any values, the useFrameCallback hook would get called way faster causing the ball to speed up considerably.

This behaviour seems to be incorrect. The speed that frames are refreshed shouldn't change because I am moving around a gesture on the screen.

Steps to reproduce

  1. Clone the reproducer repo https://github.com/friyiajr/GestureBugReproducer
  2. Run yarn
  3. Run npx expo install
  4. Start the app on iOS
  5. Allow the ball to move on its own for a few seconds so you can observe it's speed
  6. Click and drag on the screen and observe the ball gain tremendous speed

https://github.com/software-mansion/react-native-reanimated/assets/48887088/0bf26011-4744-4b0e-b708-98e8e86a9c56

Snack or a link to a repository

https://github.com/friyiajr/GestureBugReproducer/blob/main/app/(tabs)/index.tsx

Reanimated version

3.10.1

React Native version

0.74.2

Platforms

iOS

JavaScript runtime

Hermes

Workflow

Expo Go

Architecture

Paper (Old Architecture)

Build type

Debug app & dev bundle

Device

iOS simulator

Device model

No response

Acknowledgements

Yes

friyiajr commented 2 days ago

I may have solved my own problem but I'd like to know if this is a hack.

I added this line at the top so that when I move in a gesture the more frequent useFrameCallback doesn't cause things to speed up.

Basically I skip any frame updates that aren't at the even 16.6666~ milliseconds.

  useFrameCallback((frameInfo) => {
    if ((frameInfo.timeSincePreviousFrame ?? 0) < 16) {
      return;
    }
    ... the rest is the same.
Latropos commented 1 day ago

Hi! The current behaviour is expected. The hook useFrameCallback should execute per each frame, and using gestures increases the amount of frames. It enables you to immediately react to user's gesture. We already had a similar issue reported: https://github.com/software-mansion/react-native-reanimated/issues/5776

It is a bad practice to make the speed depend on frequency of frames, please use code like below:

  useFrameCallback((frameInfo) => {

    // ...

+   const SPEED = 10/16; // ten pixels per 16 miliseconds
    if (isDecending.value) {
-     circleY.value += 10
+     circleY.value += frameInfo.timeSincePreviousFrame * SPEED;
    } else {
-     circleY.value -= 10;
+     circleY.value -= frameInfo.timeSincePreviousFrame * SPEED;;
    }
  });