1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | import React, { Component } from 'react';
|
7 | import { Animated, PanResponder, View, } from 'react-native';
|
8 |
|
9 | const DEFAULT_UPPER_TRACK_COLOR = '#cccccc';
|
10 |
|
11 | const LOWEST_VALUE_THUMB_COLOR = 'white';
|
12 |
|
13 | const THUMB_SCALE_RATIO = 1.3;
|
14 |
|
15 | const THUMB_BORDER_WIDTH = 2;
|
16 |
|
17 | const TRACK_EXTRA_MARGIN_H = 5;
|
18 |
|
19 | const defaultProps = {
|
20 | radius: 6,
|
21 | disabledColor: DEFAULT_UPPER_TRACK_COLOR,
|
22 | touchPadding: 0,
|
23 | };
|
24 |
|
25 | export default class Thumb extends Component {
|
26 | constructor(props) {
|
27 | super(props);
|
28 |
|
29 | this.x = 0;
|
30 | this._radii = 0;
|
31 | this._dia = 0;
|
32 | this._containerRadii = 0;
|
33 | this._containerDia = 0;
|
34 | this._panResponder = {};
|
35 | this._animatedLeft = new Animated.Value(0);
|
36 | this._animatedScale = new Animated.Value(1);
|
37 | this._trackMarginH =
|
38 | ((props.radius || 0) + THUMB_BORDER_WIDTH) * THUMB_SCALE_RATIO + TRACK_EXTRA_MARGIN_H;
|
39 | this.state = {
|
40 | color: LOWEST_VALUE_THUMB_COLOR,
|
41 | borderColor: DEFAULT_UPPER_TRACK_COLOR,
|
42 | };
|
43 | }
|
44 | UNSAFE_componentWillMount() {
|
45 | this._panResponder = PanResponder.create({
|
46 | onStartShouldSetPanResponder: () => true,
|
47 | onStartShouldSetPanResponderCapture: () => true,
|
48 | onMoveShouldSetPanResponder: () => true,
|
49 | onMoveShouldSetPanResponderCapture: () => true,
|
50 | onPanResponderTerminationRequest: () => false,
|
51 | onShouldBlockNativeResponder: () => true,
|
52 | onPanResponderGrant: e => this.props.onGrant && this.props.onGrant(this, e),
|
53 | onPanResponderMove: e => this.props.onMove && this.props.onMove(this, e),
|
54 | onPanResponderRelease: e => this.props.onEnd && this.props.onEnd(this, e),
|
55 | onPanResponderTerminate: e => this.props.onEnd && this.props.onEnd(this, e),
|
56 | });
|
57 | this._onRadiiUpdate(this.props);
|
58 | this.setState({
|
59 | borderColor: this.props.disabledColor,
|
60 | });
|
61 | }
|
62 | componentDidMount() {
|
63 | this._animatedLeft.addListener(this._getOnSliding());
|
64 | }
|
65 | UNSAFE_componentWillReceiveProps(nextProps) {
|
66 | this._onRadiiUpdate(nextProps);
|
67 | }
|
68 | componentWillUnmount() {
|
69 | this._animatedLeft.removeAllListeners();
|
70 | }
|
71 | |
72 |
|
73 |
|
74 |
|
75 | moveTo(x) {
|
76 | this.x = x;
|
77 | const x0 = this.x + this._trackMarginH;
|
78 | Animated.parallel([
|
79 | Animated.timing(this._animatedScale, {
|
80 | toValue: THUMB_SCALE_RATIO,
|
81 | duration: 100,
|
82 | }),
|
83 | Animated.timing(this._animatedLeft, {
|
84 | toValue: x0 - this._containerRadii,
|
85 | duration: 0,
|
86 | }),
|
87 | ]).start();
|
88 | }
|
89 |
|
90 | confirmMoveTo() {
|
91 | Animated.timing(this._animatedScale, {
|
92 | toValue: 1,
|
93 | duration: 100,
|
94 | }).start();
|
95 | }
|
96 |
|
97 | render() {
|
98 |
|
99 | return (
|
100 |
|
101 | <Animated.View style={[
|
102 | this.props.style,
|
103 | {
|
104 | width: this._containerDia,
|
105 | height: this._containerDia,
|
106 | backgroundColor: this.state.borderColor,
|
107 | borderRadius: this._containerRadii,
|
108 | position: 'absolute',
|
109 | left: this._animatedLeft,
|
110 | transform: [{ scale: this._animatedScale }],
|
111 | },
|
112 | ]} {...this._panResponder.panHandlers} hitSlop={{
|
113 | top: this.props.touchPadding,
|
114 | left: this.props.touchPadding,
|
115 | bottom: this.props.touchPadding,
|
116 | right: this.props.touchPadding,
|
117 | }}>
|
118 |
|
119 | <View style={{
|
120 | width: this._dia,
|
121 | height: this._dia,
|
122 | backgroundColor: this.state.color,
|
123 | borderRadius: this._radii,
|
124 | top: THUMB_BORDER_WIDTH,
|
125 | left: THUMB_BORDER_WIDTH,
|
126 | }}/>
|
127 | </Animated.View>);
|
128 | }
|
129 |
|
130 | _onRadiiUpdate(props) {
|
131 | this._radii = props.radius || 0;
|
132 | this._dia = this._radii * 2;
|
133 | this._containerRadii = this._radii + THUMB_BORDER_WIDTH;
|
134 | this._containerDia = this._containerRadii * 2;
|
135 | }
|
136 |
|
137 | _getOnSliding() {
|
138 | let prevX = this.x;
|
139 |
|
140 |
|
141 | return ({ value }) => {
|
142 |
|
143 | const x = value + this._containerRadii - this._trackMarginH;
|
144 | if (prevX <= 0 && x > 0) {
|
145 |
|
146 | this._onExplode();
|
147 | }
|
148 | else if (prevX > 0 && x <= 0) {
|
149 |
|
150 | this._onCollapse();
|
151 | }
|
152 | prevX = x;
|
153 | };
|
154 | }
|
155 |
|
156 | _onExplode() {
|
157 | this.setState({
|
158 | borderColor: this.props.enabledColor,
|
159 | color: this.props.enabledColor,
|
160 | });
|
161 | }
|
162 |
|
163 | _onCollapse() {
|
164 | this.setState({
|
165 | borderColor: this.props.disabledColor,
|
166 | color: LOWEST_VALUE_THUMB_COLOR,
|
167 | });
|
168 | }
|
169 | }
|
170 |
|
171 | Thumb.defaultProps = defaultProps;
|
172 |
|
\ | No newline at end of file |