In this article, I'm going to demonstrate an easy approach how to build 60fps animation for a list header (e.g. image), when you are doing a bounce effect by scrolling up in the iOS application.
I use Expo workflow
since it's easy to get started and react-native-reanimated
to make beauty and smooth animations:
Install dependencies and start the project:
npm i expo-cli
expo init my-app
expo install react-native-reanimated
Then create basic markup:
export default function App() {
return (
<View style={styles.container}>
<Image
style={styles.image}
source={{ uri: 'http://picsum.photos/1000/1000' }}
/>
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
<View style={styles.items}>
{new Array(15).fill(0).map((_, index) => <View key={index} style={styles.item} />)}
</View>
</ScrollView>
</View>
);
}
const IMAGE_HEIGHT = 300
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
},
items: {
paddingTop: IMAGE_WIDTH,
paddingHorizontal: 8,
},
image: {
...StyleSheet.absoluteFillObject,
top: 0,
height: IMAGE_WIDTH,
width: '100%'
},
title: {
color: '#FFF'
},
item: {
backgroundColor: '#1C1C1C',
height: 50,
width: '100%',
marginTop: 8,
}
});
Now let's do animation:
- As you can see we use
ScrollView
andImage
fromreact-native-reanimated
to get animated nodes. - Then we set up the throttle value to
16
for theScrollView
node to throttle invocation of events. -
useValue
- hook to createAnimated.Value
. -
Animated.event
takes an array of mappings and extracts values from each arg accordingly. In our case, we extractcontentOffset.y
and assign it toscrollY
, so by native side value ofscrollY
is always the same asoffsetY
of ourAnimated.ScrollView
. - Interpolations of the
offsetY
ofAnimated.ScrollView
forAnimated.Image
:-
translateY
is needed to translate the image to the top when we are scrolling down -
scale
just nicely scales withinoutputRange
the image byinputRange
.
-
- Set
paddingTop
for the content.
export default function App() {
const scrollY = useValue(0)
return (
<View style={styles.container}>
<Animated.Image
style={{
...styles.image,
transform: [
{
translateY: interpolate(scrollY, {
inputRange: [0, IMAGE_WIDTH],
outputRange: [0, -IMAGE_WIDTH],
extrapolate: Extrapolate.CLAMP,
}),
scale: interpolate(scrollY, {
inputRange: [-IMAGE_WIDTH * 2, 0],
outputRange: [5, 1],
extrapolate: Extrapolate.CLAMP,
}),
},
],
}}
source={{ uri: 'http://picsum.photos/1000/1000' }}
/>
<Animated.ScrollView
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{ useNativeDriver: true }
)}
contentContainerStyle={{ flexGrow: 1 }}
scrollEventThrottle={16}
>
<View style={styles.items}>
{new Array(15).fill(0).map((_, index) => <View key={index} style={styles.item} />)}
</View>
</Animated.ScrollView>
</View>
);
}
That's all, hope you are well. Happy coding :)
Top comments (0)