|
// @flow |
|
|
|
import React from 'react'; |
|
import { View, Animated, TouchableOpacity, PanResponder } from 'react-native'; |
|
|
|
export type Props = { |
|
children: any, |
|
style: any, |
|
onPress: Function |
|
}; |
|
|
|
type State = { |
|
circlePosition: { x: number, y: number }, |
|
scaleAnimated: any, |
|
opacityAnimated: any, |
|
rippleRadius: number, |
|
}; |
|
|
|
class MaterialButton extends React.Component<void, Props, State>{ |
|
panResponder: any = null |
|
state: State = { |
|
circlePosition: { x: 0, y: 0 }, |
|
scaleAnimated: new Animated.Value(0), |
|
opacityAnimated: new Animated.Value(0), |
|
rippleRadius: 0, |
|
} |
|
|
|
startScaleAnimation() { |
|
this.resetAnimation(); |
|
Animated.timing(this.state.scaleAnimated, { |
|
duration: 800, |
|
toValue: 1, |
|
}).start(); |
|
} |
|
|
|
startOpacityAnimation() { |
|
Animated.timing(this.state.opacityAnimated, { |
|
duration: 600, |
|
toValue: 1, |
|
}).start(() => this.resetAnimation()); |
|
} |
|
|
|
resetAnimation() { |
|
this.state.scaleAnimated.resetAnimation(); |
|
this.state.opacityAnimated.resetAnimation(); |
|
} |
|
|
|
componentWillMount() { |
|
this.panResponder = PanResponder.create({ |
|
onStartShouldSetPanResponder: (evt, gestureState) => true, |
|
onStartShouldSetPanResponderCapture: (evt, gestureState) => false, |
|
onMoveShouldSetPanResponder: (evt, gestureState) => false, |
|
onMoveShouldSetPanResponderCapture: (evt, gestureState) => false, |
|
onPanResponderGrant: ({ nativeEvent }, gestureState) => { |
|
const { locationX, locationY } = nativeEvent; |
|
this.setState( |
|
{ circlePosition: { x: locationX, y: locationY } }, |
|
() => this.startScaleAnimation() |
|
); |
|
}, |
|
onPanResponderMove: (evt, gestureState) => { |
|
}, |
|
onPanResponderTerminationRequest: (evt, gestureState) => false, |
|
onPanResponderRelease: (evt, gestureState) => { |
|
this.props.onPress(); |
|
this.startOpacityAnimation(); |
|
}, |
|
onPanResponderTerminate: (evt, gestureState) => { |
|
this.resetAnimation(); |
|
}, |
|
onShouldBlockNativeResponder: (evt, gestureState) => { |
|
return true; |
|
}, |
|
}); |
|
} |
|
|
|
render() { |
|
const { circlePosition, rippleRadius, scaleAnimated, opacityAnimated } = this.state; |
|
return ( |
|
<View |
|
style={[this.props.style, { overflow: 'hidden' }]} |
|
onLayout={({ nativeEvent: { layout: { width, height } } }) => |
|
this.setState({ rippleRadius: Math.max(width, height) })} |
|
> |
|
{this.props.children} |
|
<Animated.View style={{ |
|
top: scaleAnimated.interpolate({ inputRange: [0, 1], outputRange: [circlePosition.y, circlePosition.y - rippleRadius] }), |
|
left: scaleAnimated.interpolate({ inputRange: [0, 1], outputRange: [circlePosition.x, circlePosition.x - rippleRadius] }), |
|
width: scaleAnimated.interpolate({ inputRange: [0, 1], outputRange: [0, rippleRadius * 2] }), |
|
height: scaleAnimated.interpolate({ inputRange: [0, 1], outputRange: [0, rippleRadius * 2] }), |
|
borderRadius: scaleAnimated.interpolate({ inputRange: [0, 1], outputRange: [0, rippleRadius] }), |
|
backgroundColor: '#00000020', |
|
position: 'absolute', |
|
opacity: opacityAnimated.interpolate({ inputRange: [0, 1], outputRange: [1, 0] }), |
|
}} /> |
|
<View style={{ position: 'absolute', height: '100%', width: '100%', backgroundColor: '#00000000' }} {...this.panResponder.panHandlers} /> |
|
</View> |
|
); |
|
} |
|
} |
|
|
|
export default MaterialButton; |
Android

iOS
