When it comes to mobile UI, animations can elevate the user experience, making interactions feel more fluid and engaging. In this blog, we’ll build an animated vertical list using react-native-reanimated that creates a smooth scrolling effect with opacity and scaling transitions.
Prerequisites
Before we start, ensure you have the following installed in your React Native project:
npm install react-native-reanimated react-native-gesture-handler
Also, update your babel.config.js file to enable Reanimated’s plugin:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['react-native-reanimated/plugin'],
};
Building the Animated Vertical List
1. Import Required Dependencies
We begin by importing essential components from react-native and react-native-reanimated:
import { Dimensions, Image, StyleSheet, Text, View } from 'react-native';
import React from 'react';
import Animated, {
interpolate,
useAnimatedScrollHandler,
useAnimatedStyle,
useSharedValue,
} from 'react-native-reanimated';
2. Define the Vertical List Component
const VerticalList = ({ data = [] }) => {
const { height } = Dimensions.get('screen');
const spacing = 8;
const itemSize = height * 0.72;
const itemFullSize = itemSize + spacing * 2;
const scrollY = useSharedValue(0);
const onScroll = useAnimatedScrollHandler(event => {
scrollY.value = event.contentOffset.y / itemFullSize;
});
We define the item size relative to the screen height and calculate spacing accordingly.
3. Create the Animated Card Component
Each item in our list will have an animated opacity and scale effect based on its position in the list.
export const DATA = [
{
id: 'V_ID_1',
title: 'Iron Man',
thumbnailUrl:
'https://ew.com/thmb/OEm1NLRTUG5HuHswbWLjvLJ5GIg=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc():format(webp)/iron-man-2000-c36c45429d5148e5a5871a887cc3d96c.jpg',
duration: '8:18',
uploadTime: 'May 9, 2011',
views: '24,969,123',
author: 'Vlc Media Player',
videoUrl:
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
audioUrl:
'https://github.com/rafaelreis-hotmart/Audio-Sample-files/raw/master/sample.mp3',
description:
'A thrilling superhero movie following Tony Stark, a billionaire genius who becomes Iron Man after creating a powerful armored suit to fight injustice.',
subscriber: '25,254,545 Subscribers',
isLive: true,
},
// rest data
]
const AnimationCard = ({ item, index, scrollY }) => {
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: interpolate(scrollY.value, [index - 1, index, index + 1], [0.3, 1, 0.3]),
transform: [
{
scale: interpolate(scrollY.value, [index - 1, index, index + 1], [0.92, 1, 0.92]),
},
],
};
});
return (
<Animated.View style={[styles.card, animatedStyle]}>
<Image source={{ uri: item.thumbnailUrl }} style={styles.backgroundImage} blurRadius={50} />
<Image source={{ uri: item.thumbnailUrl }} style={styles.thumbnail} resizeMode="cover" />
<View style={styles.textContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.description} numberOfLines={3}>{item.description}</Text>
</View>
<View style={styles.authorContainer}>
<Image source={{ uri: item.author.avatar }} style={styles.avatar} />
<Text style={styles.authorName}>{item.author.name}</Text>
</View>
</Animated.View>
);
};
4. Render the Animated List
const renderItem = ({ item, index }) => <AnimationCard item={item} index={index} scrollY={scrollY} />;
return (
<Animated.FlatList
data={data}
renderItem={renderItem}
contentContainerStyle={styles.listContainer}
snapToInterval={itemFullSize}
decelerationRate={'fast'}
onScroll={onScroll}
scrollEventThrottle={16}
/>
);
};
5. Define the Styles
To keep our component clean, let’s define a StyleSheet:
const styles = StyleSheet.create({
card: {
flex: 1,
height: Dimensions.get('screen').height * 0.72,
padding: 16,
borderRadius: 8,
gap: 8,
},
backgroundImage: {
...StyleSheet.absoluteFillObject,
borderRadius: 12,
},
thumbnail: {
flex: 1,
height: Dimensions.get('screen').height * 0.4,
},
textContainer: {
gap: 8,
},
title: {
fontSize: 24,
fontWeight: '700',
color: '#fff',
},
description: {
color: '#ddd',
},
authorContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
avatar: {
width: 24,
aspectRatio: 1,
borderRadius: 12,
},
authorName: {
fontSize: 12,
color: '#ddd',
},
listContainer: {
paddingHorizontal: 24,
paddingVertical: (Dimensions.get('screen').height - Dimensions.get('screen').height * 0.72) / 2,
gap: 16,
},
});
Final Thoughts
By using Reanimated’s useSharedValue, useAnimatedScrollHandler, and useAnimatedStyle, we’ve created a beautiful and responsive animated vertical list. This technique makes the UI more engaging and can be used for displaying articles, media content, or product listings.
Let me know in the comments if you have any questions or enhancements! 🚀
Top comments (0)