I’m working on a React Native project where I want to create a wheel-like picker with items that fade in and out as they scroll into view. I’m using react-native-reanimated for the animations, but the opacity interpolation doesn’t seem to be working as expected.
What I Have Tried:
Here is my component code:
import { StyleSheet, View, Modal } from "react-native";
import React from "react";
import { LinearGradient } from "expo-linear-gradient";
import { FlatList } from "react-native-gesture-handler";
import { horizontalScale, moderateScale, verticalScale } from "../../common";
import Text from "../atoms/Text";
import Animated, {
Extrapolation,
interpolate,
useAnimatedScrollHandler,
useAnimatedStyle,
useSharedValue,
} from "react-native-reanimated";
const ITEM_HEIGHT = 60;
const ITEM_WIDTH = 100;
const WHEEL_HEIGHT = ITEM_HEIGHT * 5;
const WheelModal = () => {
const eventY = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler((event) => {
eventY.value = event.contentOffset.y;
});
return (
<Modal transparent visible={true} animationType="fade">
<LinearGradient
locations={[0.1, 0.3, 0.7, 0.9, 1]}
colors={[
"rgba(256,256,256,.2)",
"rgba(256,256,256,.95)",
"rgba(256,256,256,1)",
"rgba(256,256,256,.1)",
"rgba(256,256,256,.2)",
]}
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Animated.FlatList
bounces={false}
showsVerticalScrollIndicator={false}
removeClippedSubviews={true}
onScroll={scrollHandler}
scrollEventThrottle={16}
data={yearGenerator()}
decelerationRate="fast"
snapToInterval={ITEM_HEIGHT}
style={{ flexGrow: 0, height: WHEEL_HEIGHT }}
contentContainerStyle={{
flexGrow: 1,
paddingVertical: ITEM_HEIGHT * 2,
}}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item, index }) => {
const animatedStyle = useAnimatedStyle(() => {
const position = index * ITEM_HEIGHT;
const opacity = interpolate(
eventY.value,
[position - 2 * ITEM_HEIGHT, position - ITEM_HEIGHT, position, position + ITEM_HEIGHT, position + 2 * ITEM_HEIGHT],
[0.1, 0.1, 1, 0.1, 0.1],
Extrapolation.CLAMP
);
return {
opacity,
};
});
return (
<Animated.View
style={[
{
height: ITEM_HEIGHT,
width: ITEM_WIDTH,
justifyContent: "center",
alignItems: "center",
},
animatedStyle,
]}
>
<Text size="lg" weight="medium" text={item.toString()} />
</Animated.View>
);
}}
/>
<View
style={{
width: ITEM_WIDTH,
height: ITEM_HEIGHT,
position: "absolute",
borderTopWidth: 1,
borderBottomWidth: 1,
}}
/>
</LinearGradient>
</Modal>
);
};
export default WheelModal;
const styles = StyleSheet.create({});
const yearGenerator = () => {
const year = new Date().getFullYear();
const years = [];
for (let i = 2015; i <= year; i++) {
years.push(i);
}
return years;
};
What I Expected:
I expect the opacity of the items to decrease as they move away from the center item, creating a fading effect.
What Actually Happens:
The opacity animation does not work, and all items have the same opacity regardless of their position in the list.
Additional Information:
React Native Version: 0.74.3
React Native Reanimated Version: 3.10.1
Screenshots:
Question:
What am I doing wrong in my animation code? How can I get the opacity interpolation to work correctly so that items fade in and out as they scroll into view?
Any help or suggestions would be greatly appreciated!