I'm trying to accomplish the vertical carousel as shown in the below gif. I'm struck by the second screen, where when the user scrolls the data from bottom to top or vice versa both the content and image change, how to achieve this? looking forward to your help?enter image description here
I have included a snack example which is mostly similar to what you want. You can use reanimated, Flatlist achieve the animation:
Code:
import * as React from 'react';
import { Text, View, StyleSheet, FlatList, Dimensions } from 'react-native';
import Constants from 'expo-constants';
import Animated, {
useSharedValue,
useAnimatedScrollHandler,
useAnimatedStyle,
interpolate,
Extrapolate,
} from 'react-native-reanimated';
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
const { width, height } = Dimensions.get('window');
const bottomHeight = height - 150 - 30 - Constants.statusBarHeight;
const data = [
{
title: 'data1',
image:
'https://assets.website-files.com/5f204aba8e0f187e7fb85a87/5f210a533185e7434d9efcab_hero%20img.jpg',
},
{
title: 'data2',
image:
'https://www.whoa.in/201604-Whoa/10-alone-broken-image-mobile-wallpaper-hd-image.jpg',
},
{
title: 'data3',
image:
'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
},
{
title: 'data4',
image:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTntlma5HASL0HAM-KiC01A-JX4MxKousAA6A&usqp=CAU',
},
];
const ImageContent = ({ image, scrollValue, index }) => {
const animatedStyle = useAnimatedStyle(() => {
const inputRange = [index * bottomHeight, (index + 1) * bottomHeight];
const translateY = interpolate(
scrollValue.value,
inputRange,
[0, -150],
Extrapolate.CLAMP
);
return {
transform: [{ translateY }],
};
});
return (
<Animated.Image
source={{ uri: image }}
style={[styles.image, { zIndex: data.length - index }, animatedStyle]}
resizeMode="cover"
/>
);
};
const TopPart = React.memo(({ scrollValue }) => {
return (
<View style={styles.topPartContainer}>
{data.map(({ image }, index) => (
<ImageContent {...{ scrollValue, image, index }} />
))}
</View>
);
});
const Item = ({ item, index }) => {
return (
<View
style={[
styles.item,
]}>
<Text style={{ color: 'red' }}>{item.title}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
topPartContainer: {
width: 150,
height: 150,
borderRadius: 75,
alignSelf: 'center',
overflow: 'hidden',
},
image: {
...StyleSheet.absoluteFillObject,
backgroundColor: '#fff',
borderRadius: 75,
},
item: {
width,
backgroundColor: '#fff',
justifyContent: 'center',
alignItems: 'center',
height: bottomHeight,
},
});
function App() {
const scrollValue = useSharedValue(0);
const handler = useAnimatedScrollHandler((event) => {
scrollValue.value = event.contentOffset.y;
});
return (
<View style={styles.container}>
<TopPart {...{ scrollValue }} />
<View style={{ flex: 1, paddingTop: 30, height: bottomHeight }}>
<AnimatedFlatList
contentContainerStyle={{ height: data.length * bottomHeight }}
showsVerticalScrollIndicator={false}
onScroll={handler}
scrollEventThrottle={16}
data={data}
pagingEnabled
keyExtractor={(item) => item.title}
renderItem={({ item, index }) => <Item {...{ item, index }} />}
/>
</View>
</View>
);
}