react-native-animated-segment-control
Version:
A custom `SegmentControl` component, pretty much similar to native's SegmentControl but with animation
172 lines (148 loc) • 4.66 kB
JavaScript
import React from 'react'
import PropTypes from 'prop-types'
import { View, Animated, Easing, ViewStyle } from 'react-native'
import styles from './styles'
import Segment from './Segment'
/**
* A custom `SegmentControl` component, pretty much similar to native's SegmentControl but with animation.
* Animates when changing the segment value.
*/
class SegmentControl extends React.Component {
constructor(props) {
super(props)
this.state = {
selectedIndex: props.selectedIndex,
segmentDimension: { width: 0, height: 0 },
activeSegmentPosition: { x: props.offsetHeight, y: props.offsetHeight },
positionAnimationValue: new Animated.Value(0)
}
}
/**
* On segment change event.
*
* @param {Number} index
*/
onSegmentSelection = index => {
const animate = () => {
Animated.timing(this.state.positionAnimationValue, {
toValue: this.state.activeSegmentPosition.x,
duration: 150,
easing: Easing.ease
}).start(() => this.props.onChange(index))
}
this.setState(
prevState => ({
selectedIndex: index,
activeSegmentPosition: { x: prevState.segmentDimension.width * index + this.props.offsetHeight, y: prevState.activeSegmentPosition.y }
}),
animate
)
}
/**
* Invoked on mount and layout change of `segmentContainer` view.
*
* @param {Object} event
*/
segmentOnLayout = event => {
const { width, height } = event.nativeEvent.layout
const segmentWidth = (width - this.props.offsetHeight * 2) / this.props.values.length
const animate = () => {
Animated.timing(this.state.positionAnimationValue, {
toValue: segmentWidth * this.state.selectedIndex + this.props.offsetHeight,
duration: 100
}).start()
}
this.setState(() => ({
segmentDimension: { width: segmentWidth, height }
}), animate)
}
render() {
const { style, disable, activeSegmentStyle, segmentControlStyle, selectedTextStyle, unSelectedTextStyle } = this.props
const { width, height } = this.state.segmentDimension
const segmentHeight = height - this.props.offsetHeight * 2
const isDisabled = disable ? 'none' : 'auto'
const extraStyles = disable ? styles.vivid : {}
return (
<View style={[styles.mainContainer, style]} pointerEvents={isDisabled}>
<View
style={[styles.segmentContainer, extraStyles, { height, borderRadius: height }, segmentControlStyle]}
onLayout={this.segmentOnLayout}
>
{this.props.values.map((segment, index) => (
<Segment
style={{ height: segmentHeight }}
title={segment}
textStyle={index !== this.state.selectedIndex ? unSelectedTextStyle : {...styles.activeText, ...selectedTextStyle}}
onPress={() => this.onSegmentSelection(index)}
/>
))}
<Animated.View
style={[
{
width,
height: segmentHeight,
left: this.state.positionAnimationValue,
top: this.state.activeSegmentPosition.y
},
styles.segment,
styles.activeSegment,
activeSegmentStyle,
]}
/>
</View>
</View>
)
}
}
SegmentControl.defaultProps = {
offsetHeight: 3,
selectedIndex: 0,
style: {},
segmentControlStyle: {},
activeSegmentStyle: {},
selectedTextStyle: {},
unSelectedTextStyle: {}
}
SegmentControl.propTypes = {
/**
* Segment values that are rendered on the view itself.
*/
values: PropTypes.arrayOf(PropTypes.string).isRequired,
/**
* To enable diable the segment control. Default value is `false`.
*/
disable: PropTypes.bool.isRequired,
/**
* A callback function of segment index on change. Changed index is send on the callback as a param.
*/
onChange: PropTypes.func.isRequired,
/**
* Index of the selected segment
*/
selectedIndex: PropTypes.number,
/**
* Active Segment's offset height. Basically a padding.
*/
offsetHeight: PropTypes.number,
/**
* Styles props of main wrapper
*/
style: PropTypes.shape(ViewStyle),
/**
* Styles props of segment control
*/
segmentControlStyle: PropTypes.shape(ViewStyle),
/**
* Styles props of active segment
*/
activeSegmentStyle: PropTypes.shape(ViewStyle),
/**
* Selected Segment text style.
*/
selectedTextStyle: PropTypes.shape(ViewStyle),
/**
* Unselected Segment text style.
*/
unSelectedTextStyle: PropTypes.shape(ViewStyle)
}
export default SegmentControl