UNPKG

react-native-komect-uikit

Version:
246 lines (216 loc) 6.57 kB
/** * @Author: will * @Date: 2017-07-24T19:34:02+08:00 * @Filename: Switch.js * @Last modified by: will * @Last modified time: 2017-08-15T15:54:50+08:00 */ import React, { Component, PropTypes } from 'react' import { ViewPropTypes, StyleSheet, Animated, Easing, Text, PanResponder, TouchableOpacity } from 'react-native' export default class extends Component { static propTypes = { width: PropTypes.number, height: PropTypes.number, value: PropTypes.bool, defaultValue: PropTypes.bool, disabled: PropTypes.bool, circleColorActive: PropTypes.string, circleColorInactive: PropTypes.string, backgroundActive: PropTypes.string, backgroundInactive: PropTypes.string, onAsyncPress: PropTypes.func, onSyncPress: PropTypes.func, style: ViewPropTypes.style, activeText: PropTypes.string, inactiveText: PropTypes.string, activeTextStyle: ViewPropTypes.style, inactiveTextStyle: ViewPropTypes.style, } static defaultProps = { width: 40, height: 21, defaultValue: false, disabled: false, circleColorActive: 'white', circleColorInactive: 'white', backgroundActive: '#43d551', backgroundInactive: '#dddddd', onAsyncPress: (callback) => {callback(true)} } constructor (props, context) { super(props, context) const { width, height } = props this.offset = width - height + 1 this.handlerSize = height - 2 const value = props.value || props.defaultValue this.state = { value, toggleable: true, alignItems: value ? 'flex-end' : 'flex-start', handlerAnimation: new Animated.Value(this.handlerSize), switchAnimation: new Animated.Value(value ? -1 : 1) } } componentWillReceiveProps (nextProps) { const { value } = this.state if (nextProps === this.props) { return } if (typeof nextProps.value !== 'undefined' && nextProps.value !== value) { this.toggleSwitch(true) } } componentWillMount () { this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: (evt, gestureState) => true, onStartShouldSetPanResponderCapture: (evt, gestureState) => true, onMoveShouldSetPanResponder: (evt, gestureState) => true, onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, onPanResponderTerminationRequest: (evt, gestureState) => true, onPanResponderGrant: this._onPanResponderGrant, onPanResponderMove: this._onPanResponderMove, onPanResponderRelease: this._onPanResponderRelease }) } _onPanResponderGrant = (evt, gestureState) => { const { disabled } = this.props if (disabled) return this.animateHandler(this.handlerSize * 6 / 5) } _onPanResponderMove = (evt, gestureState) => { const { value, toggleable } = this.state const { disabled } = this.props if (disabled) return this.setState({ toggleable: value ? (gestureState.dx < 10) : (gestureState.dx > -10) }) } _onPanResponderRelease = (evt, gestureState) => { const { handlerAnimation, toggleable, value } = this.state const { height, disabled, onAsyncPress, onSyncPress } = this.props if (disabled) return if (toggleable) { if (onSyncPress) { this.toggleSwitch(true, onSyncPress) } else { onAsyncPress(this.toggleSwitch) } } else { this.animateHandler(this.handlerSize) } } toggleSwitch = (result, callback = () => null) => { // result of async const { value, switchAnimation } = this.state const toValue = !value this.animateHandler(this.handlerSize) if (result) { this.animateSwitch(toValue, () => { callback(toValue) this.setState({ value: toValue, alignItems: toValue ? 'flex-end' : 'flex-start' }) switchAnimation.setValue(toValue ? -1 : 1) }) } } animateSwitch = (value, callback = () => null) => { const { switchAnimation } = this.state Animated.timing(switchAnimation, { toValue: value ? this.offset : -this.offset, duration: 200, easing: Easing.linear } ).start(callback) } animateHandler = (value, callback = () => null) => { const { handlerAnimation } = this.state Animated.timing(handlerAnimation, { toValue: value, duration: 200, easing: Easing.linear } ).start(callback) } _renderText(value){ if(this.props.activeText&&value){ return( <Text style={[{ position:'absolute', left:5, fontSize:14, color:'#fff', width:30, textAlign:"left", backgroundColor:'transparent'},this.props.activeTextStyle]}> {this.props.activeText} </Text> ) } else if(this.props.inactiveText&&!value){ return( <Text style={[{ position:'absolute', right:5, fontSize:14, width:30, textAlign:"right", backgroundColor:'transparent'},this.props.inactiveTextStyle]}> {this.props.inactiveText} </Text> ) } return null; } render() { const { switchAnimation, handlerAnimation, alignItems, value } = this.state const { backgroundActive, backgroundInactive, width, height, circleColorActive, circleColorInactive, style, ...rest } = this.props const interpolatedBackgroundColor = switchAnimation.interpolate({ inputRange: value ? [-this.offset, -1]: [1, this.offset], outputRange: [backgroundInactive, backgroundActive] }) const interpolatedCircleColor = switchAnimation.interpolate({ inputRange: value ? [-this.offset, -1]: [1, this.offset], outputRange: [circleColorInactive, circleColorActive] }) return ( <Animated.View {...rest} {...this._panResponder.panHandlers} style={[styles.container, style, { width, height, alignItems, borderRadius: height / 2, backgroundColor: interpolatedBackgroundColor }]}> <Animated.View style={{ backgroundColor: interpolatedCircleColor, width: handlerAnimation, height: this.handlerSize, borderRadius: height / 2, transform: [{ translateX: switchAnimation }] }}/> {this._renderText(value)} </Animated.View> ) } } const styles = StyleSheet.create({ container: { overflow: 'hidden', justifyContent: 'center' } })