Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save mattThousand/6203be7e18f37a9bb8c5e70e6bce4ad6 to your computer and use it in GitHub Desktop.

Select an option

Save mattThousand/6203be7e18f37a9bb8c5e70e6bce4ad6 to your computer and use it in GitHub Desktop.
Tapping an individual dot animates the flatlist's scroll position.
import React, {FunctionComponent, useRef} from 'react';
import {
Animated,
Dimensions,
FlatList,
Image,
Pressable,
StyleSheet,
View,
} from 'react-native';
const images: string[] = [
'https://media.tenor.com/images/3704f2b9b8b66a5747116f436a5e6aba/tenor.gif',
'https://media.tenor.com/images/78feaf65c477ead06981dca74c3ead14/tenor.gif',
'https://media.tenor.com/images/425213c8ada06900931c2d0213389ae4/tenor.gif',
];
const {width} = Dimensions.get('screen');
const CustomPageControl: FunctionComponent = () => {
const animatedValue = useRef(new Animated.Value(0)).current;
const flatListRef = useRef<FlatList>();
const handleDotPress = (index) => {
flatListRef.current.scrollToIndex({animated: true, index});
};
return (
<View style={style.container}>
<View style={style.topContainer}>
<FlatList
ref={flatListRef}
data={images}
horizontal
showsHorizontalScrollIndicator={false}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: animatedValue}}}],
{useNativeDriver: false},
)}
pagingEnabled={true}
keyExtractor={(_, index) => index.toString()}
renderItem={({item}) => {
return (
<View style={style.imageContainer}>
<Image style={style.image} source={{uri: item}} />
</View>
);
}}
/>
</View>
<View style={style.bottomContainer}>
<FlatList
horizontal
data={images}
keyExtractor={(_, index) => index.toString()}
renderItem={({index}) => {
const inputRange = [
(index - 1) * width,
index * width,
(index + 1) * width,
];
const colorOutputRange = ['#000', 'cyan', '#000'];
const scaleOutputRange = [1, 2, 1];
const dotScale = animatedValue.interpolate({
inputRange,
outputRange: scaleOutputRange,
extrapolate: 'clamp',
});
const color = animatedValue.interpolate({
inputRange,
outputRange: colorOutputRange,
extrapolate: 'clamp',
});
return (
<View style={style.dotContainer}>
<PagingDot
index={index}
onPress={handleDotPress}
color={color}
scale={dotScale}
/>
</View>
);
}}
/>
</View>
</View>
);
};
const PagingDot: FunctionComponent<{scale; color; index; onPress}> = ({
scale,
color,
index,
onPress,
}) => {
return (
<Pressable onPress={() => onPress(index)}>
<Animated.View
style={[
style.pagingDot,
{backgroundColor: color, transform: [{scale}]},
]}
/>
</Pressable>
);
};
const style = StyleSheet.create({
container: {
flex: 3,
},
topContainer: {
flex: 2,
},
bottomContainer: {
flex: 1,
width,
justifyContent: 'flex-start',
alignItems: 'center',
},
imageContainer: {
justifyContent: 'flex-end',
paddingBottom: 40,
alignItems: 'center',
width,
},
image: {
width: width - 80,
height: 300,
borderRadius: 40,
},
pagingDot: {
width: 14,
height: 14,
backgroundColor: 'cyan',
borderRadius: 7,
borderWidth: 2,
borderColor: '#000',
},
dotContainer: {
width: 50,
padding: 10,
},
});
export default CustomPageControl;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment