React Native Animations | Circle Animated List

In a recent React Native project I was asked to visualize a list of items in a way a little more appealing than just rendering each item with no animations at all. So there was a screen where I have to show all links of user.

CONCEPT

The concept was to make an unlimited scrollable rounded circular list. it should be dynamic so on run-time anyone can Increase or decrease Circles. I tried using react-native-circle-list but it wasn’t worth it.

So, I used React native animated library with Flatlist to make it work. First defined a random data for testing

const [data, setData] = useState([
{ "id": 0, "value": "C1CD2F", "name": 'Football' },
{ "id": 1, "value": "360EF5", "name": 'Salsa' },
{ "id": 2, "value": "51BB75", "name": 'Chess' },
{ "id": 3, "value": "340A57", "name": 'Snooker' },
{ "id": 4, "value": "99E11F", "name": 'Dancing' },
{ "id": 5, "value": "937F07", "name": 'Coding' },
{ "id": 6, "value": "A341B9", "name": 'Dating' },
{ "id": 7, "value": "BC8BFB", "name": 'Making' },
{ "id": 8, "value": "ADC304", "name": 'Cars Show' },
{ "id": 9, "value": "4496C2", "name": 'Race' },
{ "id": 10, "value": 'A0B2BC', "name": 'Cooking' },
{ "id": 11, "value": "DC4460", "name": 'Riding' },
{ "id": 12, "value": "E391CA", "name": 'Cinema' },
{ "id": 13, "value": '707500', "name": 'Dancing' },
{ "id": 14, "value": "ADA323", "name": 'Football' },
{ "id": 15, "value": "D18572", "name": 'Coding' },
{ "id": 16, "value": 'A341B9', "name": 'Fitness' },
])

I have used react-native-responsive-screen for height and width for designs responsiveness you can also pass static sizes. and with using Animated.Flatlist.

import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-native-responsive-screen';const ITEM_SIZE = wp(30);
const SPACE_ITEM_SIZE = (hp(68) - ITEM_SIZE) / 2;
const PROFILE_ITEM_SIZE = (hp(92) - ITEM_SIZE) / 2;
const CONTACTS_ITEM_SIZE = (hp(65) - ITEM_SIZE) / 2;
const scrollY = useRef(new Animated.Value(0)).current;
const animatedValue = useRef(new Animated.Value(0)).current;
<Animated.FlatList
horizontal
showsVerticalScrollIndicator={false}
data={data}
keyExtractor={keyExtractor}
renderItem={renderItem}
contentContainerStyle={{ alignItems: 'center' }}
snapToInterval={ITEM_SIZE}
decelerationRate={0.4}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{ useNativeDriver: true }
)}
scrollEventThrottle={16}
/>

Render Item functions

Here i used translateX for giving margins to circles Vertically so then can get a little merging into each other or reducing spaces between them. defined inputRange1 where index-1 according to ITEM_SIZE is the Centred index of the circle, As I have also added a empty white circle so the can get centred.

In translateY is used for giving margins to circles horizontally Input Range, so they can get into a circluar shape. defined inputRange2 where index-1 according to ITEM_SIZE is the Centred index of the circle, As I have also added a empty white circle so the can get centred.

For increasing/decreasing circles sizes I have used scale

after defining all input and output ranges i have defined the transform value in View style props

transform: [{ translateX }, { scale }, { translateY }]

following is the renderItem code:

const renderItem = ({ item, index }) => {const inputRange2 = [
(index - 5) * ITEM_SIZE,
(index - 4) * ITEM_SIZE,
(index - 3) * ITEM_SIZE,
(index - 2) * ITEM_SIZE,
(index - 1) * ITEM_SIZE,
index * ITEM_SIZE,
(index + 1) * ITEM_SIZE,
(index + 2) * ITEM_SIZE,
(index + 3) * ITEM_SIZE,
]
const inputRange = [ (index - 4) * ITEM_SIZE,
(index - 3) * ITEM_SIZE,
(index - 2) * ITEM_SIZE,
(index - 1) * ITEM_SIZE,
index * ITEM_SIZE,
(index + 1) * ITEM_SIZE,
(index + 2) * ITEM_SIZE
]
const translateX = scrollY.interpolate({
inputRange,
outputRange: [-150, -40, 70, 120, 70, -40, -150]
})
const translateY = scrollY.interpolate({
inputRange: inputRange2,
outputRange: [-400, -170, -30, 20, 0, -20, 30, 170, 400]
})
const scale = scrollY.interpolate({
inputRange,
outputRange: [0.6, 0.8, 1, 1.3, 1, 0.8, 0.6]
})
if (!item.value) {
return (
<View style={{ height: SPACE_ITEM_SIZE }} />
)
} else {
return (
<Animated.View
key='image0'
style={[styles.image, {
width: wp("30%"),
height: ITEM_SIZE,
transform: [{ translateX }, { scale }, { translateY }],
justifyContent: 'center',
alignItems: 'center',
borderRadius: wp("17%"),
backgroundColor: `#${item.value}`,
}]}>
<Pressable
onPress={() => props.navigation.navigate('SendInvite')}
style={[styles.circle, {
width: wp("30%"),
height: ITEM_SIZE,
borderWidth: 0,
borderRadius: wp("17%"),
backgroundColor: `#${item.value}`,
width: wp("29.8%"), height: wp("29%"),
justifyContent: 'center', alignItems: 'center',
}]}>
<Text style={[styles.text]}>{item.name}</Text>
<View
style={{
flexDirection: 'row', alignItems: 'center',
marginTop: wp(2),
}}>
<Text style={[styles.text]}>{index}</Text>
<Feather style={{ marginLeft: wp(1) }}
name="user-plus" size={18} color="#fff" />
</View>
</Pressable>
</Animated.View>
)}};

Hope it helped you to learn react native animations

As always if you find this helpful share and press the 👏🏻 button so that others can find it too. If you see a typo feel free to highlight it or if you’re stuck drop a comment and I’ll try my best to help you.

https://www.buymeacoffee.com/osamakhan

https://www.buymeacoffee.com/osamakhan

All my tutorials are free but if you feel like supporting you can buymeacoffee.com/osamakhan

Happy Coding 👨🏻‍💻

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Osama khan

Osama khan

Cross-Platform Mobile Application Developer