UNPKG

6.09 kBJavaScriptView Raw
1/**
2 * The `Thumb` part of {@link Slider} and {@link RangeSlider} components.
3 *
4 * Created by awaidman on 16/1/21.
5 */
6import React, { Component } from 'react';
7import { Animated, PanResponder, View, } from 'react-native';
8// Default color of the upper part of the track
9const DEFAULT_UPPER_TRACK_COLOR = '#cccccc';
10// Color of the thumb when lowest value is chosen
11const LOWEST_VALUE_THUMB_COLOR = 'white';
12// The max scale of the thumb
13const THUMB_SCALE_RATIO = 1.3;
14// Width of the thumb border
15const THUMB_BORDER_WIDTH = 2;
16// extra spacing enlarging the touchable area
17const TRACK_EXTRA_MARGIN_H = 5;
18/** Default props, see {@link ThumbProps} */
19const defaultProps = {
20 radius: 6,
21 disabledColor: DEFAULT_UPPER_TRACK_COLOR,
22 touchPadding: 0,
23};
24/** `Thumb` component of {@link Slider} and {@link RangeSlider}. */
25export default class Thumb extends Component {
26 constructor(props) {
27 super(props);
28 /** current x-axis position */
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 * animate the sliding
73 * @param x target position, relative to the track
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 /** stop sliding */
90 confirmMoveTo() {
91 Animated.timing(this._animatedScale, {
92 toValue: 1,
93 duration: 100,
94 }).start();
95 }
96 /** {@inheritDoc @types/react#Component.render} */
97 render() {
98 // noinspection JSSuspiciousNameCombination
99 return (
100 // the outer circle to draw the border
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 // when thumb radii updated, re-calc the dimensions
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 // return a memoized function to handle sliding animation events
137 _getOnSliding() {
138 let prevX = this.x; // memorize the previous x
139 // on sliding of the thumb
140 // `value` - the `left` of the thumb, relative to the container
141 return ({ value }) => {
142 // convert to value relative to the track
143 const x = value + this._containerRadii - this._trackMarginH;
144 if (prevX <= 0 && x > 0) {
145 // leaving the lowest value, scale up the thumb
146 this._onExplode();
147 }
148 else if (prevX > 0 && x <= 0) {
149 // at lowest value, scale down the thumb
150 this._onCollapse();
151 }
152 prevX = x;
153 };
154 }
155 // from 'lowest' to 'non-lowest'
156 _onExplode() {
157 this.setState({
158 borderColor: this.props.enabledColor,
159 color: this.props.enabledColor,
160 });
161 }
162 // from 'non-lowest' to 'lowest'
163 _onCollapse() {
164 this.setState({
165 borderColor: this.props.disabledColor,
166 color: LOWEST_VALUE_THUMB_COLOR,
167 });
168 }
169}
170/** Defaults, see {@link defaultProps} */
171Thumb.defaultProps = defaultProps;
172//# sourceMappingURL=Thumb.js.map
\No newline at end of file