UNPKG

4.47 kBJavaScriptView Raw
1import React, { PropTypes, Component } from 'react';
2import { View, Animated, Easing, PanResponder } from 'react-native'
3import { styles, radius } from './styles.js';
4
5export default class Ripple extends Component {
6 static defaultProps = {
7 rippleColor: 'black',
8 rippleOpacity: 0.20,
9 rippleDuration: 400,
10 rippleSize: 0,
11 rippleContainerBorderRadius: 0,
12 disabled: false,
13 };
14
15 static propTypes = {
16 rippleColor: PropTypes.string,
17 rippleOpacity: PropTypes.number,
18 rippleDuration: PropTypes.number,
19 rippleSize: PropTypes.number,
20 rippleContainerBorderRadius: PropTypes.number,
21 disabled: PropTypes.bool,
22 };
23
24 constructor(props) {
25 super(props);
26
27 this.unique = 0;
28 this.focused = false;
29
30 this.state = {
31 size: 0,
32 width: 0,
33 height: 0,
34 ripples: [],
35 };
36 }
37
38 componentWillMount() {
39 this.panResponder = PanResponder.create({
40 onStartShouldSetPanResponder: () => !this.props.disabled,
41 onMoveShouldSetPanResponder: () => !this.props.disabled,
42 onPanResponderTerminationRequest: () => false,
43
44 onPanResponderGrant: (event, gestureState) => {
45 this.setFocused(true);
46 },
47
48 onPanResponderMove: (event, gestureState) => {
49 let { locationX, locationY } = event.nativeEvent;
50 let { width, height } = this.state;
51
52 let focused =
53 (locationX >= 0 && locationX <= width) &&
54 (locationY >= 0 && locationY <= height);
55
56 this.setFocused(focused);
57 },
58
59 onPanResponderRelease: (event, gestureState) => {
60 let { onPress, disabled } = this.props;
61
62 if (this.focused && !disabled) {
63 this.startRipple(event);
64
65 if (typeof onPress === 'function') {
66 onPress();
67 }
68 }
69
70 this.setFocused(false);
71 },
72
73 onPanResponderTerminate: (event, gestureState) => {
74 this.setFocused(false);
75 },
76 });
77 }
78
79 setFocused(focused) {
80 if (focused ^ this.focused) {
81 this.focused = focused;
82 this.onFocusChage();
83 }
84 }
85
86 onFocusChage() {
87 let { onPressOut, onPressIn } = this.props;
88
89 if (this.focused) {
90 if (typeof onPressIn === 'function') {
91 onPressIn();
92 }
93 } else {
94 if (typeof onPressOut === 'function') {
95 onPressOut();
96 }
97 }
98 }
99
100 onLayout(event) {
101 let { rippleSize } = this.props;
102 let { width, height } = event.nativeEvent.layout;
103
104 let size = rippleSize > 0?
105 rippleSize : Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
106
107 this.setState({ size, width, height });
108 }
109
110 startRipple(event) {
111 let { locationX, locationY } = event.nativeEvent;
112 let { rippleDuration, rippleOpacity } = this.props;
113 let { ripples, size } = this.state;
114
115 let unique = this.unique++;
116
117 let ripple = {
118 scale: new Animated.Value(1 / (radius * 2)),
119 opacity: new Animated.Value(rippleOpacity),
120
121 unique, locationX, locationY,
122 };
123
124 ripples.push(ripple);
125
126 Animated
127 .parallel([
128 Animated.timing(ripple.scale, {
129 toValue: size / (radius * 2),
130 duration: rippleDuration,
131 easing: Easing.out(Easing.ease),
132 }),
133
134 Animated.timing(ripple.opacity, {
135 toValue: 0,
136 duration: rippleDuration,
137 easing: Easing.out(Easing.ease),
138 }),
139 ])
140 .start(() => {
141 let ripples = this.state.ripples.slice(1);
142
143 this.setState({ ripples });
144 });
145
146 this.setState({ ripples });
147 }
148
149 render() {
150 let { children, rippleColor, rippleContainerBorderRadius, ...props } = this.props;
151 let { ripples } = this.state;
152
153 let containerStyle = {
154 borderRadius: rippleContainerBorderRadius,
155 };
156
157 ripples = ripples
158 .map(({ scale, opacity, locationX, locationY, unique }) => {
159 let rippleStyle = {
160 top: locationY - radius,
161 left: locationX - radius,
162 backgroundColor: rippleColor,
163
164 transform: [{ scale }],
165 opacity,
166 };
167
168 return (
169 <Animated.View style={[ styles.ripple, rippleStyle ]} key={unique} pointerEvents='none' />
170 );
171 });
172
173 return (
174 <Animated.View onLayout={this.onLayout.bind(this)} {...props} {...this.panResponder.panHandlers}>
175 {children}
176
177 <View style={[ styles.container, containerStyle ]} pointerEvents='none'>
178 {ripples}
179 </View>
180 </Animated.View>
181 );
182 }
183}