software-mansion / react-native-reanimated

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

List item sliding removal animation works on iOS but not on Android with the same code #5111

Closed phanghos closed 1 year ago

phanghos commented 1 year ago

Description

Recently at work I was faced with the challenge of displaying a horizontal list of items with each card consisting of an image, some text, and an X icon to remove it from the list. When the X button is clicked to remove a card from the list, it (and adjacent cards to its right if any) should smoothly slide and transition to the left to their new positions. If removing the last item from the list, the one on its right (if any), should smoothly slide and transition to its right to its new position. This is working fine on iOS but, as you will see from the reproducible example, there's a blank space, and I'm not able to pinpoint the problem. What I'm doing is that when the user clicks on the X, I trigger the animations with a certain duration. Then I call the function to remove from the list with a delay of the same value as the animation duration, so that the actual item removal happens after the animation. This is where it's failing on Android.

I've linked a repo I created with a minimum, reproducible example of the issue, but the magic is mainly happening here:

const translateX = useDerivedValue(() => {
    if (removingIndex === undefined) {
      return 0;
    }

    const isRemovingLast = removingIndex === totalItemsCount - 1;

    const isSecondToLastAndIsRemovingLast = isSecondToLast && isRemovingLast;

    const shouldMoveToTheLeft = index >= removingIndex;

    return isSecondToLastAndIsRemovingLast || shouldMoveToTheLeft
      ? isSecondToLastAndIsRemovingLast
        ? withTiming(cardWidth + 8, {
            duration: ANIMATION_DURATION,
            easing: Easing.linear,
          })
        : withTiming(-cardWidth - 8, {
            duration: ANIMATION_DURATION,
            easing: Easing.linear,
          })
      : 0;
  });

  const animatedStyle = useAnimatedStyle(() => ({
    zIndex:
      removingIndex === undefined ? index : index === removingIndex ? 1 : 2,
    transform: [{ translateX: translateX.value }],
  }));

Steps to reproduce

Android (example app)

https://github.com/software-mansion/react-native-reanimated/assets/1209351/b9c75fa3-d822-4fd7-94db-87c137a7e78e

iOS working fine (example app)

https://github.com/software-mansion/react-native-reanimated/assets/1209351/ff39ba20-8131-4149-bece-375998100e75

Snack or a link to a repository

https://github.com/phanghos/ReanimatedSlidingRemovalAnimation

Reanimated version

3.5.3

React Native version

0.72.4

Platforms

Android

JavaScript runtime

Hermes

Workflow

React Native

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

Android emulator

Device model

No response

Acknowledgements

Yes

Latropos commented 1 year ago

Hi! Your example contains a lot of mistakes, please see our documentation or copy our solution: https://github.com/software-mansion/react-native-reanimated/blob/main/app/src/examples/LayoutAnimations/AnimatedList.tsx