rn-arc-slider
Version:
React Native Component for arc slider using React Native Svg
93 lines (91 loc) • 4.44 kB
JavaScript
import React from "react";
import { PanResponder, View } from "react-native";
import Svg, { Path, Circle, G, Text } from "react-native-svg";
const CircularSlider = ({
/** prop1 description */
thumbRadius = 12, trackRadius = 100, trackWidth = 5, trackTintColor = "#e1e8ee", trackColor = "#2089dc", value = 0, minValue = 0, maxValue = 100, minAngle = 0, maxAngle = 359.9, onChange, thumbTextColor = "white", thumbTextSize = 10, noThumb = false, showText = false, showThumbText = false, thumbColor = "#2089dc", textColor = "#2089dc", textSize = 80, }) => {
const location = React.useRef({ x: 0, y: 0 });
const viewRef = React.useRef(null);
const valuePercentage = ((value - minValue) * 100) / maxValue;
const { current: panResponder } = React.useRef(PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: () => location.current.x && location.current.y,
onPanResponderMove: (_e, { moveX, moveY }) => {
let angle = cartesianToPolar(moveX - location.current.x + trackRadius + thumbRadius, moveY - location.current.y + trackRadius + thumbRadius);
if (angle <= minAngle) {
onChange === null || onChange === void 0 ? void 0 : onChange(minAngle / 3.6);
}
else if (angle >= maxAngle) {
onChange === null || onChange === void 0 ? void 0 : onChange(maxAngle / 3.6);
}
else {
onChange === null || onChange === void 0 ? void 0 : onChange(angle / 3.6);
}
},
}));
const polarToCartesian = React.useCallback((angleToChange) => {
let r = trackRadius;
let hC = trackRadius + thumbRadius;
let a = ((angleToChange - 90) * Math.PI) / 180.0;
let x = hC + r * Math.cos(a);
let y = hC + r * Math.sin(a);
return { x, y };
}, [trackRadius, thumbRadius]);
const cartesianToPolar = React.useCallback((x, y) => {
let hC = trackRadius + thumbRadius;
if (x === 0) {
return y > hC ? 0 : 180;
}
else if (y === 0) {
return x > hC ? 90 : 270;
}
else {
return (Math.round((Math.atan((y - hC) / (x - hC)) * 180) / Math.PI) +
(x > hC ? 90 : 270));
}
}, [trackRadius, thumbRadius]);
const width = (trackRadius + thumbRadius) * 2;
const startCoord = polarToCartesian(0);
const endCoord = polarToCartesian(valuePercentage * 3.6);
const endTintCoord = polarToCartesian(maxAngle);
return (<View style={{ width, height: width }} ref={viewRef} onLayout={() => {
var _a;
(_a = viewRef.current) === null || _a === void 0 ? void 0 : _a.measure((x, y, w, h, px, py) => {
location.current = {
x: px + w / 2,
y: py + h / 2,
};
});
}}>
<Svg width={width} height={width} ref={viewRef}>
<Path stroke={trackTintColor} strokeWidth={trackWidth} d={[
"M",
startCoord.x,
startCoord.y,
"A",
trackRadius,
trackRadius,
0,
maxAngle <= 180 ? "0" : "1",
1,
endTintCoord.x,
endTintCoord.y,
].join(" ")}/>
<Path stroke={trackColor} strokeWidth={trackWidth} fill="none" d={`M${startCoord.x} ${startCoord.y} A ${trackRadius} ${trackRadius} 0 ${valuePercentage * 3.6 > 180 ? 1 : 0} 1 ${endCoord.x} ${endCoord.y}`}/>
{showText && (<Text x={trackRadius + thumbRadius} y={trackRadius + 40} fontSize={textSize} fill={textColor} textAnchor="middle">
{Math.ceil(value).toString()}
</Text>)}
{!noThumb && (<G x={endCoord.x - thumbRadius} y={endCoord.y - thumbRadius}>
<Circle r={thumbRadius} cx={thumbRadius} cy={thumbRadius} fill={thumbColor} {...panResponder.panHandlers}/>
{showThumbText && (<Text x={thumbRadius} y={thumbRadius + thumbTextSize / 2} fontSize={10} fill={thumbTextColor} textAnchor="middle">
{Math.ceil(value).toString().padStart(2, "0")}
</Text>)}
</G>)}
</Svg>
</View>);
};
CircularSlider.defaultProps = {};
export default CircularSlider;