Skip to content

Instantly share code, notes, and snippets.

@VojtaSim
Created June 8, 2019 16:30
Show Gist options
  • Save VojtaSim/6203b217f3819c96ab0de2f1b37427a9 to your computer and use it in GitHub Desktop.
Save VojtaSim/6203b217f3819c96ab0de2f1b37427a9 to your computer and use it in GitHub Desktop.
react-navigation: Shazam-like TabBar with dots
import React, { Component } from 'react';
import { View, SafeAreaView, Animated } from 'react-native';
import { NavigationState } from 'react-navigation';
import { SceneRendererProps } from 'react-native-tab-view';
import Styles, { DOT_SIZE, DOT_MARGIN, BAR_WIDTH } from './styles';
type ScreenDotsProps = SceneRendererProps & {
state: NavigationState
};
type ScreenDotsState = {
barWidth: Animated.AnimatedInterpolation,
barPosition: Animated.AnimatedSubtraction,
}
export default class ScreenDots extends Component<ScreenDotsProps, ScreenDotsState> {
constructor(props) {
super(props);
const barWidth = this.getBarWithInterpolation();
this.state = {
barWidth,
barPosition: this.getBarPositionInterpolation(barWidth)
};
}
render() {
const { routes } = this.props.navigation.state;
const { barWidth, barPosition } = this.state;
return (
<SafeAreaView style={Styles.container}>
<View style = {Styles.dotsContainer}>
{routes.map((route, index) => (
<View key={route.key} style={[
Styles.dot,
index === 0 ? null : { marginLeft: DOT_MARGIN }
]} />
))}
<Animated.View style = {[
Styles.bar,
{
width: barWidth,
left: barPosition
}
]}/>
</View>
</SafeAreaView>
)
}
getBarWithInterpolation() {
const { position, navigation } = this.props;
const { routes } = navigation.state;
const indexPhase = [0.3, 0.7, 1];
const widthPhase = [BAR_WIDTH, BAR_WIDTH, DOT_SIZE];
const routeIndexes = routes.reduce(
(indexes, _, index) => indexes.concat(indexPhase.map(i => index + i)),
[0]
);
return position.interpolate({
inputRange: routeIndexes,
outputRange: routes.reduce(phases => phases.concat(widthPhase), [DOT_SIZE]),
extrapolate: 'clamp'
});
}
getBarPositionInterpolation(widthInterpolation) {
const { position, navigation } = this.props;
const { routes } = navigation.state;
const routeIndexes = routes.map((_, index) => index);
return Animated.subtract(
position.interpolate({
inputRange: routeIndexes,
outputRange: routes.map((_, index) => (DOT_SIZE + DOT_MARGIN) * index),
extrapolate: 'clamp'
}),
Animated.divide(widthInterpolation, 2)
);
}
}
import { StyleSheet } from 'react-native';
export const DOT_SIZE = 10;
export const DOT_MARGIN = 14;
export const BAR_WIDTH = DOT_SIZE + DOT_MARGIN;
export default StyleSheet.create({
container: {
position: 'absolute',
left: 0,
top: 0,
right: 0,
flexDirection: 'row',
flexWrap: 'nowrap',
justifyContent: 'center',
zIndex: 100,
},
dotsContainer: {
position: 'relative',
flexDirection: 'row',
flexWrap: 'nowrap',
justifyContent: 'center'
},
dot: {
backgroundColor: '#000',
borderRadius: DOT_SIZE,
height: DOT_SIZE,
width: DOT_SIZE,
opacity: 0.5
},
bar: {
backgroundColor: '#000',
borderRadius: DOT_SIZE,
height: DOT_SIZE,
opacity: 1,
position: 'absolute',
bottom: 0,
marginLeft: DOT_SIZE / 2
},
indicator: {
position: 'absolute',
bottom: 0,
left: 0,
backgroundColor: 'red',
height: 3,
}
});
@Angelm116
Copy link

Hello, how do I use this? I want to implement it with react-native-tab-view. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment