IjzerenHein / react-native-shared-element

Native shared element transition "primitives" for react-native 💫
MIT License
2.21k stars 97 forks source link

FastImage/ImageBackground animation is wrong when borderWidth > 0 is used #6

Closed brunocrpontes closed 4 years ago

brunocrpontes commented 5 years ago

Using FastImage as a shared element show inconsistency while transitioning only in Android.

With FastImage: FastImage-Component-Transition

With Image: Image-Component-Transition

Simulator Environment: version: Android 10

IjzerenHein commented 5 years ago

Alright, thanks for reporting this and the animated-gifs! I'm expecting to be back at shared-element development within the next 2 weeks. Cheers

IjzerenHein commented 4 years ago

Hi! I've been trying to reproduce this problem but so far have been unable to. I tested on an Android 10 and 9 simulator environment using the Example app. I will need some more information to reproduce this. Could you share your shared-element configuration as well as the styles and props?

brunocrpontes commented 4 years ago

Sure!

Previous Item:

import React, {PureComponent} from 'react'
import {View, StyleSheet, Text} from 'react-native'
import {
  Row,
  MenuItemImage,
  MenuItemTitle,
  MenuItemDescription,
  MenuItemPrice,
} from './StyledComponents'
import {
  TouchableRipple,
  TouchableRipplePropsWithoutChildren,
} from 'react-native-paper'
import {SharedElement} from 'react-navigation-shared-element'
import MenuProductItemLoader from './MenuProductItemLoader'
import FastImage from 'react-native-fast-image'
import {formatToBRL} from '../utils/functions'

const styles = StyleSheet.create({
  container: {
    paddingHorizontal: 16,
    paddingVertical: 8,
    alignItems: 'flex-start',
  },
  image: {
    width: 72,
    height: 72,
    marginRight: 16,
    borderWidth: 1,
    borderRadius: 4,
    borderColor: '#e5e5e5',
  },
  infoContainer: {
    flex: 1,
    justifyContent: 'space-between',
  },
  headerContainer: {
    height: 20,
  },
})

interface Props extends TouchableRipplePropsWithoutChildren {
  id: number
  name: string
  image: string
  value: number
  description?: string
}

export default class MenuProductItem extends PureComponent<Props> {
  static Loader = MenuProductItemLoader
  static HEIGHT = 88

  render() {
    const {image, id, description, name, value, ...rest} = this.props
    return (
      <TouchableRipple {...rest}>
        <View pointerEvents='box-only' style={styles.container}>
          <Row>
            <SharedElement id={`product.image.${id}`}>
              <FastImage
                resizeMode={FastImage.resizeMode.contain}
                style={styles.image}
                source={{uri: image}}
              />
            </SharedElement>
            <View style={styles.infoContainer}>
              <MenuItemTitle numberOfLines={1}>{name}</MenuItemTitle>
              <MenuItemDescription numberOfLines={2}>
                {description}
              </MenuItemDescription>
            </View>
          </Row>
          <MenuItemPrice style={{alignSelf: 'flex-end'}}>
            {formatToBRL(value)}
          </MenuItemPrice>
        </View>
      </TouchableRipple>
    )
  }
}

Next Item:

import React from 'react'
import {
  NavigationStackScreenProps,
  NavigationStackProp,
} from 'react-navigation-stack'
import {Product} from '../typescript/models'
import {SharedElement} from 'react-navigation-shared-element'
import FastImage from 'react-native-fast-image'
import {StyleSheet, Dimensions} from 'react-native'

const {width} = Dimensions.get('screen')

const styles = StyleSheet.create({
  image: {
    width,
    height: (width / 16) * 9,
    borderWidth: 0,
    borderRadius: 0,
  },
})

interface Params {
  product: Product
}

export default function ProductScreen(
  props: NavigationStackScreenProps<Params>,
) {
  const {navigation} = props
  const product = navigation.getParam('product')

  return (
    <SharedElement id={`product.image.${product.id}`}>
      <FastImage
        resizeMode={FastImage.resizeMode.cover}
        style={styles.image}
        source={{uri: product.image}}
      />
    </SharedElement>
  )
}

ProductScreen.sharedElements = (
  navigation: NavigationStackProp<{}, Params>,
) => {
  const product = navigation.getParam('product')

  return [{id: `product.image.${product.id}`}]
}
janpe commented 4 years ago

I am seeing this issue on iOS as well.

IjzerenHein commented 4 years ago

Awesome, thanks for the info @brunocrpontes Will go check it out 👍

@janpe Which version of react-native-shared-element are you using? I recently released 5.0.2 which fixes some issues on >=RN60 on iOS, also with FastImage.

janpe commented 4 years ago

I have the latest versions of this library as well as react-native and FastImage.

IjzerenHein commented 4 years ago

@brunocrpontes I've investigated the problem and found the cause. It was related to the use of borderWidth which affected the size of the inner image-view (FastImage wraps the Image inside an outer <View> which displays the borders). This in turn caused the image-view detection to not work correctly and it animated the outer-view instead, leading to the stretching that you saw. I've fixed it and released a new version, https://github.com/IjzerenHein/react-native-shared-element/releases/tag/v0.5.3

IjzerenHein commented 4 years ago

@janpe I just tested on iOS and I also see a similar problem on iOS when borderWidth is used. Did you also see the problem when using borderWidth > 0?

IjzerenHein commented 4 years ago

ezgif-6-93eb577fb089

brunocrpontes commented 4 years ago

Thanks @IjzerenHein for the fixes! 😄

its happening here too, after transition the element is recalculated without the border

iOS Shared Element Border Transition

IjzerenHein commented 4 years ago

Hey guys. I've just released v0.5.4 which fixes several issues on iOS which, were introduced in RN60+. Please upgrade and let me know whether that solves the problem for you. cheers!

https://github.com/IjzerenHein/react-native-shared-element/releases/tag/v0.5.4

IjzerenHein commented 4 years ago

Closing, as this should now all be fixed

farahty commented 4 years ago

hi @IjzerenHein , i have the same issue in android , the image get resized , IOS works perfectly

react-native-fast-image@8.1.5 react-native@0.62-rc.05 react-navigation/core@5.2.2 react-navigation/native@5.0.9 react-navigation/stack@5.1.1 react-native-shared-element@0.5.6 react-navigation-shared-element@5.0.0-alpha1

navigation trigger


const Card = ({item}) => {
  const {styles} = useStyles();
  const navigation = useNavigation();
  return (
    <View>
      <View>
        <SharedElement id={item.id}>
          <Image
            source={{uri: item.image}}
            style={{width: screen.width, height: 200, borderWidth: 0}}
          />
        </SharedElement>
        <SharedElement
          id={`gradient-${item.id}`}
          style={StyleSheet.absoluteFill}>
          <Gradient
            colors={['rgba(0,0,0,0.4)', 'rgba(0,0,0,0.1)']}
            style={StyleSheet.absoluteFill}
          />
        </SharedElement>
        <RectButton
          style={StyleSheet.absoluteFill}
          onPress={() => {
            navigation.navigate('Page', {item});
          }}
        />
      </View>
      <Text style={styles.title}>{item.name}</Text>
    </View>
  );
};

the second page

export const Page = () => {
  const route = useRoute();
  const {item} = route.params;
  const {styles} = useStyles();
  return (
    <View>
      <View>
        <SharedElement id={item.id}>
          <Image
            resizeMode="cover"
            style={styles.image}
            source={{uri: item.image}}
          />
        </SharedElement>
        <SharedElement id={`gradient-${item.id}`} style={styles.fill}>
          <Gradient
            style={styles.fill}
            colors={['rgba(0,0,0,0.4)', 'rgba(0,0,0,0.1)']}
          />
        </SharedElement>
      </View>
      <View style={styles.cardBody}>
        <Text style={styles.title}>{item.title}</Text>
        <Text style={styles.description}>{item.description}</Text>
      </View>
    </View>
  );
};

Navigator Settings

import React from 'react';
import {createSharedElementStackNavigator} from 'react-navigation-shared-element';
import {Page} from './UberPage';
import {CardList} from './UberTaps';

const {Navigator, Screen} = createSharedElementStackNavigator();

export default () => {
  return (
    <Navigator
      screenOptions={{
        title: '',
        headerTransparent: true,
        cardStyleInterpolator: ({current: {progress}}) => {
          const opacity = progress.interpolate({
            inputRange: [0, 1],
            outputRange: [0, 1],
            extrapolate: 'clamp',
          });
          return {cardStyle: {opacity}};
        },
      }}>
      <Screen name="CardList" component={CardList} />
      <Screen
        name="Page"
        component={Page}
        sharedElementsConfig={route => {
          const {item} = route.params;
          return [{id: item.id, resize: 'stretch'}, `gradient-${item.id}`];
        }}
      />
    </Navigator>
  );
};

Regards

IjzerenHein commented 4 years ago

@nimerfarahty Can you share a video?

farahty commented 4 years ago

ezgif com-video-to-gif

farahty commented 4 years ago

hi @IjzerenHein i have tested again with react native image instead of FastImage and also remove the Gradient Shared element complete but without success

Regards

farahty commented 4 years ago

hi @IjzerenHein should i add a new issue ? regards

IjzerenHein commented 4 years ago

@nimerfarahty I noticed that the first Image (in the Card Component) doesn't have a resizeMode. Could you try setting that and see whether it makes any difference?

farahty commented 4 years ago

@IjzerenHein no any difference

Svarto commented 4 years ago

@nimerfarahty I have a similar problem, did you find a solution?

art1373 commented 4 years ago

I got this issue to wrong resize and scale is applied in the end node

stewartmcgown commented 4 years ago

Here's my version of the same problem: https://i.imgur.com/PqQkKzI.mp4

Resizemethod, resizemode all set but, as the debug shows, shared element thinks the target size is different from what it actually is.

anhquan291 commented 2 years ago

Hi guys I'm still facing the same issue with FastImage on Android. iOS works perfectly. Any suggestions? Thanks a lot

ahmadzraiq commented 2 years ago

@IjzerenHein if I do something like that resizeMode={"stretch"} it will work perfectly