Skip to content
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

[Android][Animation] Interpolated translateY value causes android view to jump. #21801

Open
mmamoyco opened this issue Oct 15, 2018 · 125 comments
Open
Labels

Comments

@mmamoyco
Copy link

mmamoyco commented Oct 15, 2018

Environment

React Native Environment Info:
System:
OS: macOS 10.14
CPU: x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
Memory: 862.68 MB / 16.00 GB
Shell: 3.2.57 - /bin/sh
Binaries:
Node: 10.9.0 - ~/.nvm/versions/node/v10.9.0/bin/node
Yarn: 1.10.1 - ~/.nvm/versions/node/v10.9.0/bin/yarn
npm: 6.2.0 - ~/.nvm/versions/node/v10.9.0/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 11.2, macOS 10.13, tvOS 11.2, watchOS 4.2
IDEs:
Android Studio: 3.1 AI-173.4819257
Xcode: 9.2/9C40b - /usr/bin/xcodebuild
npmPackages:
react: 16.6.0-alpha.8af6728 => 16.6.0-alpha.8af6728
react-native: ^0.57.3 => 0.57.3
npmGlobalPackages:
react-native-cli: 2.0.1

Description

I'm trying to implement collapsible header using React Native Animated API. using event method i get AnimatedHeaderValue and interpolate it into translateY value.
This value i apply to container of my listView (so listView moves vertically).
IOS animation works perfectly, but Android animation jumping and lagging when i scroll. I tried to inscrease scroll value and Android animation became more smooth.

This is scrollView container that passes onScroll to scrollView (listView)

<ScCompanyNewsFeedList optimizeHeight getRef={scrollView => { console.log("SCROLL VIEW", scrollView) this._scrollView = scrollView; }} scrollEventThrottle = { 2 } onScroll={Animated.event( [{ nativeEvent: { contentOffset: { y: this.AnimatedHeaderValue }}}], )} companyId={this.props.companyId}/> }

this is base container that contains tabs and my scrollView. It's moving when scroll.

<Animated.View style={[{flex: 1}, {transform: [{translateY: headerHeight}]}]}> ... </Animated.View>

Interpolation

const animationRange = this.AnimatedHeaderValue.interpolate({ inputRange: [0, scrollRange], outputRange: [0, 1], extrapolate: "clamp" }); const headerHeight2 = animationRange.interpolate({ inputRange: [0, 1], outputRange: [0, -200] });

@react-native-bot
Copy link
Collaborator

It looks like you are using an older version of React Native. Please update to the latest release, v0.57 and verify if the issue still exists.

The ":rewind:Old Version" label will be removed automatically once you edit your original post with the results of running react-native info on a project using the latest release.

@mmamoyco
Copy link
Author

The issue still exists on rn 0.57

@darylalexjohnson
Copy link

@godofmadness You ever resolve this issue?

@mmamoyco
Copy link
Author

@darylalexjohnson nope, issue still exist.

@lindesvard
Copy link

Having similar issues..

@d2dindustries
Copy link

d2dindustries commented Jan 26, 2019

I am also having this issue. Any resolution in sight?

My guess is that Y translating the scrollview while scrolling throws off the difference calculation between the start scrollY coordinate and it's relative current scrollY coordinate. I assume this calculation is how the scrollview component does it's scrolling animation. If it's relative point is changing along with the y translation, that would explain the jumping.

@mateusfontana
Copy link

I'm also having this issue using RN 0.57.8.

It looks like it only happens when you have a decreasing outputRange, like [100, 0], or in OP's case, [0, -200]. It also happens with animated margins and paddings.

@kelset
Copy link
Contributor

kelset commented Jan 28, 2019

Does this still happen on 0.58?

@mateusfontana
Copy link

@kelset Yes. Here is a example tested on RN 58.1:

import React, { Component } from "react";
import { Animated, View, StyleSheet } from "react-native";

export default class Example extends Component {
  state = {
    scrollY: new Animated.Value(0)
  };

  render() {
    const marginTopAnimated = this.state.scrollY.interpolate({
      inputRange: [0, 100],
      outputRange: [100, 0],
      extrapolate: "clamp"
    });

    return (
      <View style={{ flex: 1, backgroundColor: "skyblue" }}>
        <Animated.ScrollView
          style={[
            styles.scrollView,
            {
              transform: [{ translateY: marginTopAnimated }]
            }
          ]}
          scrollEventThrottle={16}
          onScroll={Animated.event(
            [
              {
                nativeEvent: { contentOffset: { y: this.state.scrollY } }
              }
            ],
            { useNativeDriver: true }
          )}
        >
          <View style={{ height: 1000 }} />
        </Animated.ScrollView>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  scrollView: {
    backgroundColor: "#FFF",
    borderTopRightRadius: 28,
    borderTopLeftRadius: 28
  }
});

Just create a new project with react-native init and paste it on App.js. Here is a gif of the result:

example-animation

@benevbright
Copy link

benevbright commented Feb 5, 2019

Hi, I'm maintaining react-navigation-collapsible.
This issue is critical for my module and I tried many things for a workaround but no luck.

@benevbright
Copy link

#15445
It has not been fixed for a long time though.

@kelset
Copy link
Contributor

kelset commented Feb 5, 2019

Are there any PRs open to try and fix this? Or commits already on master?

@benevbright
Copy link

benevbright commented Feb 5, 2019

Can anyone tell me how to trigger re-compile react-native Android native? I want to try modifying some files.
But It seems that it's using a build from somewhere else.
./gladlew clean nor deleting android/app/build do not work.
Even I created a new project and deleted a couple of native java file just to break building, but it succeeds to build the project!

@hramos hramos removed the Bug Report label Feb 6, 2019
@davidkalosi

This comment has been minimized.

@httol

This comment has been minimized.

@d2dindustries

This comment has been minimized.

@stockrel
Copy link

stockrel commented Mar 7, 2019

+1

2 similar comments
@ddedic
Copy link

ddedic commented Mar 8, 2019

+1

@imyagnesh
Copy link

+1

@droplessjake
Copy link

Having a similar problem currently with an Animated.View that uses translateY.
It jumps about when the page reloads or anything causes it to update.

Screenrecorder-2021-11-04-14-14-31-174.0.mp4

@chareefdev
Copy link

chareefdev commented Nov 24, 2021

0.66.3 same only android

@ddikodroid
Copy link
Contributor

Are there any PRs open to try and fix this? Or commits already on master?

nope

@Deodes
Copy link

Deodes commented Jul 26, 2022

I was trying to implement collapsible header using reanimated and meet this dumb problem on Android. No matter what I use to animate ScrollView (paddingTop, translateY, height). This is quite serious bug and it exists for a really long time, I don't believe that using react-native I cannot implement such an easy animation on Android...

It has redproduced since 2017: #15445

If there aren't any blocks, contributors please pay attention to this problem

@KingAmo
Copy link

KingAmo commented Jul 26, 2022

@Deodes maybe you could try react-native-reanimated instead of Animated ?

@Deodes
Copy link

Deodes commented Jul 26, 2022

@KingAmo I updated my comment

@bekatd
Copy link

bekatd commented Jul 27, 2022

Why this silly bug exists for a such really long time?

@yogendrajs
Copy link

still a bug!!!

@matiaswastaken
Copy link

Any updates on this? Just ran into this problem on react-native 0.69.5.

@poojan010
Copy link

can not make animatable header with Tab View
due to this bug

@Mohamedghaly140
Copy link

we are in 2023 and still a bug, the UI gets vibrated while scrolling on the Android platform RN 0.71.4
when this bug may be resolved !!
i hope to be resolved as soon as possible, it's critical one

@baesumin
Copy link

+1

@MDG-MMeksasi
Copy link

MDG-MMeksasi commented Jun 17, 2023

It's really weird that this bug still exist till the day, I managed to overcome it like that so I noticed that the scrollview was bouncing by the height of the translateY, so you can use these conditions on android also you will need the marginBottom on the view of the list to hide the white part on the bottom when translating hope this will help.

import Animated, {
  Extrapolate,
  interpolate,
  useAnimatedScrollHandler,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';

const AnimatedFlashList = Animated.createAnimatedComponent(FlashList);

  const scrollY = useSharedValue(0);
  const headerHeight = 90; // Height of the header
  const progress = useSharedValue(0);
  const translateTheshold = 50; // Threshold to start the animation
  const scrollHandler = useAnimatedScrollHandler(event => {
    const offsetY = event.contentOffset.y;

    // scroll down
    if (offsetY > scrollY.value && progress.value === 0) {
      if (offsetY > translateTheshold) {
        progress.value = withTiming(1, {
          duration: 500,
        });
         if (Platform.OS === 'android') {
          // subtract header height to scrollY value to fix android bounce issue
          scrollY.value =
            scrollY.value - headerHeight < 0 ? 0 : scrollY.value - headerHeight;
        }
      }
      // scroll up
    } else if (offsetY < scrollY.value && progress.value === 1) {
     if (Platform.OS === 'android') {
        // add header height to scrollY value to fix android bounce issue
        scrollY.value = scrollY.value + headerHeight;
      }
      progress.value = withTiming(0, {
        duration: 500,
      });
    }

    scrollY.value = withTiming(offsetY);
  });

  const translateYStyle = useAnimatedStyle(() => {
    return {
      transform: [
        {
          translateY: interpolate(
            progress.value,
            [0, 1],
            [0, -headerHeight],
            Extrapolate.CLAMP,
          ),
        },
      ],
    };
  });

   <View>
      <Animated.View
        style={[
            {
              backgroundColor: 'red',
              height: 90,
              alignItems: 'center',
              justifyContent: 'center',
            },
            translateYStyle,
          ]}>
        <Text preset="header">hello header</Text>
      </Animated.View>

      <Animated.View
        style={[{flex: 1, marginBottom: -headerHeight}, translateYStyle]}>
        <AnimatedFlashList
          showsVerticalScrollIndicator={false}
          bounces={false}
          scrollEventThrottle={16}
          onScroll={scrollHandler}
          data={claim.attributes.groups}
          renderItem={renderItem}
          estimatedItemSize={350}
        />
      </Animated.View>
  </View>

Even more I made it a hook so it's reusable

import {Platform} from 'react-native';
import {
  useSharedValue,
  useAnimatedScrollHandler,
  withTiming,
  useAnimatedStyle,
  interpolate,
  Extrapolate,
} from 'react-native-reanimated';

interface IUseTranslateHeaderOnScrollProps {
  translateTheshold: number;
  headerHeight: number;
}

export const useTranslateHeaderOnScroll = ({
  headerHeight,
  translateTheshold,
}: IUseTranslateHeaderOnScrollProps) => {
  const scrollY = useSharedValue(0);
  const progress = useSharedValue(0);

  const scrollHandler = useAnimatedScrollHandler(event => {
    const offsetY = event.contentOffset.y;

    // scroll down
    if (offsetY > scrollY.value && progress.value === 0) {
      if (offsetY > translateTheshold) {
        progress.value = withTiming(1, {
          duration: 500,
        });
        if (Platform.OS === 'android') {
          // subtract header height to scrollY value to fix android bounce issue
          scrollY.value =
            scrollY.value - headerHeight < 0 ? 0 : scrollY.value - headerHeight;
        }
      }
      // scroll up
    } else if (offsetY < scrollY.value && progress.value === 1) {
      if (Platform.OS === 'android') {
        // add header height to scrollY value to fix android bounce issue
        scrollY.value = scrollY.value + headerHeight;
      }
      progress.value = withTiming(0, {
        duration: 500,
      });
    }
    scrollY.value = withTiming(offsetY);
  });

  const translateYStyle = useAnimatedStyle(() => {
    return {
      transform: [
        {
          translateY: interpolate(
            progress.value,
            [0, 1],
            [0, -headerHeight - 10],
            Extrapolate.CLAMP,
          ),
        },
      ],
    };
  });

  return {
    translateYStyle,
    scrollHandler,
  };
};

So you can use it in the screen like this

const headerHeight = 80; // Height of the header
const translateTheshold = 50; // Threshold to start the animation

  const {translateYStyle, scrollHandler} = useTranslateHeaderOnScroll({
    headerHeight,
    translateTheshold,
  });

@caiopngoncalves
Copy link

@MDG-MMeksasi could you explain how your solution works? I'm having a hard time trying to understand

@jvfalco1
Copy link

I'm facing this same issue on RN 0.71.5

@MDG-MMeksasi
Copy link

MDG-MMeksasi commented Jun 23, 2023

@jvfalco1 @caiopngoncalves did you try my solution? could you try it please just to see if it works properly

@caiopngoncalves
Copy link

@MDG-MMeksasi Yeah, It works. I just wanted to understand more about the problem

@jvfalco1
Copy link

@jvfalco1 @caiopngoncalves did you try my solution? could you try it please just to see if it works properly

Actually dont, because Im facing the same issue but Im not translating it, Im reducing the Height of the component.

@MDG-MMeksasi
Copy link

MDG-MMeksasi commented Jun 23, 2023

@jvfalco1

it should be better if you target translateY, otherwise, it should always be laggy on android

@jvfalco1
Copy link

@MDG-MMeksasi Even with translation I get the same error, Unfortunately I have to use the height. Unfortunately I think ill not be able to fix this

@jmazier-j2d
Copy link

https://stackoverflow.com/questions/59658059/react-native-animation-screen-shaking-when-scrolling-slowing

Had the same issue that I've "fixed" following the upvote comment there. It has to do with interpolation ratio, seems like android doesn't like when dealing with decimals on that side...

@guvenkaranfil
Copy link

It blocks our apps release . Is there any update on this issue 🤔

@abolfazlyounesi

This comment was marked as duplicate.

1 similar comment
@H4mxa
Copy link

H4mxa commented Feb 23, 2024

+1

@yosukapro
Copy link

Any answer for this? It since 5 years and still don't see the solution

@react-native-bot
Copy link
Collaborator

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@react-native-bot react-native-bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Sep 9, 2024
@iamolegga
Copy link

.

@react-native-bot react-native-bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label Sep 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests