UNPKG

fleetback-react-native-progress

Version:

Progress indicators and spinners for React Native using ReactART

179 lines (167 loc) 4.83 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Animated, Easing, View, I18nManager } from 'react-native'; const INDETERMINATE_WIDTH_FACTOR = 0.3; const BAR_WIDTH_ZERO_POSITION = INDETERMINATE_WIDTH_FACTOR / (1 + INDETERMINATE_WIDTH_FACTOR); export default class ProgressBar extends Component { static propTypes = { animated: PropTypes.bool, borderColor: PropTypes.string, borderRadius: PropTypes.number, borderWidth: PropTypes.number, children: PropTypes.node, color: PropTypes.string, height: PropTypes.number, indeterminate: PropTypes.bool, indeterminateAnimationDuration: PropTypes.number, onLayout: PropTypes.func, progress: PropTypes.number, style: PropTypes.any, unfilledColor: PropTypes.string, width: PropTypes.number, useNativeDriver: PropTypes.bool, animationConfig: PropTypes.object, animationType: PropTypes.oneOf(['decay', 'timing', 'spring']), }; static defaultProps = { animated: true, borderRadius: 4, borderWidth: 1, color: 'rgba(0, 122, 255, 1)', height: 6, indeterminate: false, indeterminateAnimationDuration: 1000, progress: 0, width: 150, useNativeDriver: false, animationConfig: { bounciness: 0 }, animationType: 'spring', }; constructor(props) { super(props); const progress = Math.min(Math.max(props.progress, 0), 1); this.state = { width: 0, progress: new Animated.Value( props.indeterminate ? INDETERMINATE_WIDTH_FACTOR : progress ), animationValue: new Animated.Value(BAR_WIDTH_ZERO_POSITION), }; } componentDidMount() { if (this.props.indeterminate) { this.animate(); } } componentDidUpdate(prevProps) { if (prevProps.indeterminate !== this.props.indeterminate) { if (this.props.indeterminate) { this.animate(); } else { Animated.spring(this.state.animationValue, { toValue: BAR_WIDTH_ZERO_POSITION, useNativeDriver: this.props.useNativeDriver, }).start(); } } if ( prevProps.indeterminate !== this.props.indeterminate || prevProps.progress !== this.props.progress ) { const progress = this.props.indeterminate ? INDETERMINATE_WIDTH_FACTOR : Math.min(Math.max(this.props.progress, 0), 1); if (this.props.animated) { const { animationType, animationConfig } = this.props; Animated[animationType](this.state.progress, { ...animationConfig, toValue: progress, useNativeDriver: this.props.useNativeDriver, }).start(); } else { this.state.progress.setValue(progress); } } } handleLayout = event => { if (!this.props.width) { this.setState({ width: event.nativeEvent.layout.width }); } if (this.props.onLayout) { this.props.onLayout(event); } }; animate() { this.state.animationValue.setValue(0); Animated.timing(this.state.animationValue, { toValue: 1, duration: this.props.indeterminateAnimationDuration, easing: Easing.linear, isInteraction: false, useNativeDriver: this.props.useNativeDriver, }).start(endState => { if (endState.finished) { this.animate(); } }); } render() { const { borderColor, borderRadius, borderWidth, children, color, height, style, unfilledColor, width, ...restProps } = this.props; const innerWidth = Math.max(0, width || this.state.width) - borderWidth * 2; const containerStyle = { width, borderWidth, borderColor: borderColor || color, borderRadius, overflow: 'hidden', backgroundColor: unfilledColor, }; const progressStyle = { backgroundColor: color, height, transform: [ { translateX: this.state.animationValue.interpolate({ inputRange: [0, 1], outputRange: [innerWidth * -INDETERMINATE_WIDTH_FACTOR, innerWidth], }), }, { translateX: this.state.progress.interpolate({ inputRange: [0, 1], outputRange: [innerWidth / (I18nManager.isRTL ? 2 : -2), 0], }), }, { // Interpolation a temp workaround for https://github.com/facebook/react-native/issues/6278 scaleX: this.state.progress.interpolate({ inputRange: [0, 1], outputRange: [0.0001, 1], }), }, ], }; return ( <View style={[containerStyle, style]} onLayout={this.handleLayout} {...restProps} > <Animated.View style={progressStyle} /> {children} </View> ); } }