UNPKG

3.33 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 };
13
14 static propTypes = {
15 rippleColor: PropTypes.string,
16 rippleOpacity: PropTypes.number,
17 rippleDuration: PropTypes.number,
18 rippleSize: PropTypes.number,
19 rippleContainerBorderRadius: PropTypes.number,
20 };
21
22 constructor(props) {
23 super(props);
24
25 this.unique = 0;
26
27 this.state = {
28 size: 0,
29 ripples: [],
30 };
31 }
32
33 componentWillMount() {
34 this.panResponder = PanResponder.create({
35 onStartShouldSetPanResponder: () => {
36 return true;
37 },
38
39 onPanResponderGrant: (event, gestureState) => {
40 let { onPressIn } = this.props;
41
42 if (typeof onPressIn === 'function') {
43 onPressIn();
44 }
45 },
46
47 onPanResponderRelease: (event, gestureState) => {
48 let { onPressOut } = this.props;
49
50 if (typeof onPressOut === 'function') {
51 onPressOut();
52 }
53
54 this.startRipple(event);
55 }
56 });
57 }
58
59 onLayout(event) {
60 let { rippleSize } = this.props;
61 let { width, height } = event.nativeEvent.layout;
62
63 let size = rippleSize > 0?
64 rippleSize : Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
65
66 this.setState({ size });
67 }
68
69 startRipple(event) {
70 let { locationX, locationY } = event.nativeEvent;
71 let { rippleDuration, rippleOpacity } = this.props;
72 let { ripples, size } = this.state;
73
74 let unique = this.unique++;
75
76 let ripple = {
77 scale: new Animated.Value(1 / (radius * 2)),
78 opacity: new Animated.Value(rippleOpacity),
79
80 unique, locationX, locationY,
81 };
82
83 ripples.push(ripple);
84
85 Animated
86 .parallel([
87 Animated.timing(ripple.scale, {
88 toValue: size / (radius * 2),
89 duration: rippleDuration,
90 easing: Easing.out(Easing.ease),
91 }),
92
93 Animated.timing(ripple.opacity, {
94 toValue: 0,
95 duration: rippleDuration,
96 easing: Easing.out(Easing.ease),
97 }),
98 ])
99 .start(() => {
100 let ripples = this.state.ripples.slice(1);
101
102 this.setState({ ripples });
103 });
104
105 this.setState({ ripples });
106 }
107
108 render() {
109 let { children, rippleColor, rippleContainerBorderRadius, ...props } = this.props;
110 let { ripples } = this.state;
111
112 ripples = ripples
113 .map(($_) => {
114 let { scale, opacity, unique } = $_;
115
116 let style = {
117 top: $_.locationY - radius,
118 left: $_.locationX - radius,
119 backgroundColor: rippleColor,
120
121 transform: [{ scale }],
122 opacity,
123 };
124
125 return (
126 <Animated.View style={[ styles.ripple, style ]} key={unique} pointerEvents='none' />
127 );
128 });
129
130 return (
131 <Animated.View onLayout={this.onLayout.bind(this)} {...props} {...this.panResponder.panHandlers}>
132 {children}
133
134 <View style={[styles.container, { borderRadius: rippleContainerBorderRadius }]}>
135 {ripples}
136 </View>
137 </Animated.View>
138 );
139 }
140}