software-mansion / react-native-reanimated

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

Animated.View shaking when synchronizing with Animated.ScrollView (Android) #6708

Open fedpre opened 2 hours ago

fedpre commented 2 hours ago

Description

When using the event.contentOffset.y from useAnimatedScrollHandler in a Animated.ScrollView to make the view sticky at the top, the view is shaking instead of just being there. This behaviour started to happen with 0.76.0 and the new architecture enabled. When I disable the new architecture, the behaviour returns to normal.

In order to test if my implementation has some weird things, I created a brand new project with the react native community cli on RN 0.76.2, new arch enabled.

This is all the code from App.tsx

https://github.com/user-attachments/assets/9ea43cbe-b9a7-46f3-a66c-19486c518dda

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 */

import React from 'react';
import type {PropsWithChildren} from 'react';
import {
  SafeAreaView,
  StatusBar,
  StyleSheet,
  Text,
  useColorScheme,
  View,
} from 'react-native';
import Animated, {
  useAnimatedScrollHandler,
  useAnimatedStyle,
  useSharedValue,
} from 'react-native-reanimated';

import {
  Colors,
  DebugInstructions,
  Header,
  LearnMoreLinks,
  ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';

type SectionProps = PropsWithChildren<{
  title: string;
}>;

function Section({children, title}: SectionProps): React.JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';
  return (
    <View style={styles.sectionContainer}>
      <Text
        style={[
          styles.sectionTitle,
          {
            color: isDarkMode ? Colors.white : Colors.black,
          },
        ]}>
        {title}
      </Text>
      <Text
        style={[
          styles.sectionDescription,
          {
            color: isDarkMode ? Colors.light : Colors.dark,
          },
        ]}>
        {children}
      </Text>
    </View>
  );
}

function App(): React.JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';
  const scrollProgressY = useSharedValue(0);

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  const onScrollHandler = useAnimatedScrollHandler(event => {
    scrollProgressY.value = event.contentOffset.y;
  });

  const rHeaderStyle = useAnimatedStyle(() => {
    return {
      transform: [
        {
          translateY: scrollProgressY.value,
        },
      ],
    };
  });

  return (
    <SafeAreaView style={backgroundStyle}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={backgroundStyle.backgroundColor}
      />
      <Animated.ScrollView
        contentInsetAdjustmentBehavior="automatic"
        onScroll={onScrollHandler}
        scrollEventThrottle={16}
        style={backgroundStyle}>
        <Header />
        <Animated.View
          style={[{position: 'absolute', zIndex: 999}, rHeaderStyle]}>
          <Text>hello world</Text>
        </Animated.View>
        <View
          style={{
            backgroundColor: isDarkMode ? Colors.black : Colors.white,
          }}>
          <Section title="Step One">
            Edit <Text style={styles.highlight}>App.tsx</Text> to change this
            screen and then come back to see your edits.
          </Section>
          <Section title="See Your Changes">
            <ReloadInstructions />
          </Section>
          <Section title="Debug">
            <DebugInstructions />
          </Section>
          <Section title="Learn More">
            Read the docs to discover what to do next:
          </Section>
          <LearnMoreLinks />
        </View>
      </Animated.ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
  },
  highlight: {
    fontWeight: '700',
  },
});

export default App;

Steps to reproduce

  1. Create a new project
  2. Add reanimated
  3. Keep track of the scrolling in an Animated.ScrollView with useAnimatedScrollHandler
  4. Assign that value to the transform translate Y to make it sticky
  5. Notice that on android is starts shaking.

Snack or a link to a repository

https://github.com/fedpre/reanimated-shaking-test

Reanimated version

3.16.1

React Native version

0.76.2

Platforms

Android

JavaScript runtime

None

Workflow

None

Architecture

Fabric (New Architecture)

Build type

None

Device

None

Device model

No response

Acknowledgements

Yes

fedpre commented 2 hours ago

I woul also add that it is happening slightly on iOS too (see video below).

https://github.com/user-attachments/assets/62359725-6e24-49b8-ae6b-cd2ab1448a7a