343 lines
9.8 KiB
JavaScript
343 lines
9.8 KiB
JavaScript
import { View, Text, PanResponder, Dimensions } from "react-native";
|
||
import React, { useState, useEffect, useRef } from "react";
|
||
import { useTailwind } from "tailwind-rn";
|
||
export default function MySlider({
|
||
lower_bound,
|
||
upper_bound,
|
||
step,
|
||
unit,
|
||
unitSite = "right",
|
||
width = Dimensions.get("window").width - 44,
|
||
onChange,
|
||
handleFunc = () => {},
|
||
disabled = false,
|
||
thumbSize = 25,
|
||
stepValues = [],
|
||
hasInfinity,
|
||
leftValue = lower_bound,
|
||
rightValue = stepValues[stepValues.length - 1],
|
||
maximumTrackTintColor = "#dcdbdb",
|
||
minimumTrackTintColor = "#577BFF",
|
||
processHeight = 7,
|
||
}) {
|
||
// const leftProcess = useRef(0);
|
||
// const rightProcess = useRef(0);
|
||
|
||
const [leftProcess, setLeftProcess] = useState(0);
|
||
const [rightProcess, setRightProcess] = useState(0);
|
||
const [stepNum, setStepNum] = useState(stepValues.length - 1);
|
||
const [processWidth, setProcessWidth] = useState(330);
|
||
const tailwind = useTailwind();
|
||
const leftWatcher = useRef(null);
|
||
const rightWatcher = useRef(null);
|
||
const currentLeftValue = useRef(leftValue);
|
||
const currentRightValue = useRef(rightValue);
|
||
useEffect(() => {
|
||
leftWatcher.current = PanResponder.create({
|
||
// 建立监视器
|
||
onStartShouldSetPanResponder: () => true,
|
||
onPanResponderGrant: _onPanResponderGrantLeft, // 按下
|
||
onPanResponderMove: _onPanResponderMoveLeft, // 移动
|
||
onPanResponderEnd: _onPanResponderEndLeft, // 结束
|
||
});
|
||
rightWatcher.current = PanResponder.create({
|
||
// 建立监视器
|
||
onStartShouldSetPanResponder: () => true,
|
||
onPanResponderGrant: _onPanResponderGrantRight, // 按下
|
||
onPanResponderMove: _onPanResponderMoveRight, // 移动
|
||
onPanResponderEnd: _onPanResponderEndRight, // 结束
|
||
});
|
||
setProcessWidth(width - thumbSize * 2);
|
||
}, []);
|
||
useEffect(() => {
|
||
// if(itemKey)
|
||
const currentLeftProcess =
|
||
(stepValues.indexOf(leftValue) * step - lower_bound) /
|
||
(upper_bound - lower_bound);
|
||
const currentRightProcess =
|
||
(stepValues.indexOf(rightValue) * step - lower_bound) /
|
||
(upper_bound - lower_bound);
|
||
setLeftProcess(currentLeftProcess);
|
||
setRightProcess(currentRightProcess);
|
||
currentLeftValue.current = leftValue;
|
||
currentRightValue.current = rightValue;
|
||
}, [leftValue, rightValue]);
|
||
// 左侧滑块事件
|
||
const _onPanResponderGrantLeft = () => {
|
||
handleFunc && handleFunc();
|
||
};
|
||
|
||
const _onPanResponderEndLeft = () => {
|
||
handleFunc && handleFunc();
|
||
};
|
||
const _onPanResponderMoveLeft = (e, gestureState) => {
|
||
const process =
|
||
(gestureState.x0 - thumbSize * 2 + gestureState.dx) / processWidth;
|
||
_changeProcess(process, "left");
|
||
};
|
||
|
||
// 右侧滑块事件
|
||
const _onPanResponderGrantRight = () => {
|
||
handleFunc && handleFunc();
|
||
};
|
||
const _onPanResponderEndRight = () => {
|
||
handleFunc && handleFunc();
|
||
};
|
||
|
||
const _onPanResponderMoveRight = (e, gestureState) => {
|
||
const process =
|
||
(gestureState.x0 - thumbSize / 2 + gestureState.dx) / processWidth;
|
||
_changeProcess(process, "right");
|
||
};
|
||
const _changeProcess = (changeProcess, direction) => {
|
||
// 判断滑动开关
|
||
if (disabled) return;
|
||
// 按步长比例变化刻度
|
||
changeProcess =
|
||
changeProcess > 1 ? 1 : changeProcess < 0 ? 0 : changeProcess;
|
||
const v = changeProcess * (upper_bound - lower_bound);
|
||
const newValue = Math.round(v / step) * step;
|
||
const newProcess = Math.round(newValue) / (upper_bound - lower_bound);
|
||
setStepNum(stepNum + 1);
|
||
if (process !== newProcess) {
|
||
if (direction == "left") {
|
||
const currentRightProcess =
|
||
(stepValues.indexOf(currentRightValue.current) * step - lower_bound) /
|
||
(upper_bound - lower_bound);
|
||
if (newProcess < currentRightProcess) {
|
||
const newLower =
|
||
stepValues[
|
||
Math.round(
|
||
(newProcess * (upper_bound - lower_bound) + lower_bound) / step
|
||
)
|
||
];
|
||
if (newLower <= currentRightValue.current - 3) {
|
||
setLeftProcess(newProcess);
|
||
setStepNum((old) => old + 1);
|
||
onChange({
|
||
lower_bound: newLower,
|
||
upper_bound: currentRightValue.current,
|
||
});
|
||
}
|
||
}
|
||
} else {
|
||
const currentLeftProcess =
|
||
(stepValues.indexOf(currentLeftValue.current) * step - lower_bound) /
|
||
(upper_bound - lower_bound);
|
||
|
||
if (newProcess > currentLeftProcess) {
|
||
const newUpper =
|
||
stepValues[
|
||
Math.round(
|
||
(newProcess * (upper_bound - lower_bound) + lower_bound) / step
|
||
)
|
||
];
|
||
if (newUpper >= currentLeftValue.current + 3) {
|
||
setRightProcess(newProcess);
|
||
setStepNum((old) => old - 1);
|
||
onChange({
|
||
lower_bound: currentLeftValue.current,
|
||
upper_bound: newUpper,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
return (
|
||
<View
|
||
style={{
|
||
height: "upper_bound-content",
|
||
width,
|
||
// flex: 1,
|
||
flexDirection: "row",
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
marginBottom: 36,
|
||
marginTop: 24,
|
||
}}
|
||
>
|
||
<View style={{ width: "100%", position: "relative" }}>
|
||
{/* 格子步数 */}
|
||
{hasInfinity && (
|
||
<View
|
||
style={{
|
||
...tailwind("flex flex-row justify-between items-center"),
|
||
position: "absolute",
|
||
top: -23,
|
||
flex: 1,
|
||
width: "100%",
|
||
padding: 17,
|
||
paddingRight: 20,
|
||
marginLeft: -12,
|
||
}}
|
||
>
|
||
{stepValues.map((item, index) => (
|
||
<View
|
||
key={index}
|
||
style={{
|
||
backgroundColor:
|
||
item >= leftValue && item <= rightValue
|
||
? minimumTrackTintColor
|
||
: maximumTrackTintColor,
|
||
width: thumbSize / 2,
|
||
height: thumbSize / 2,
|
||
borderRadius: 50,
|
||
}}
|
||
></View>
|
||
))}
|
||
</View>
|
||
)}
|
||
<View
|
||
style={{
|
||
backgroundColor: maximumTrackTintColor,
|
||
height: processHeight,
|
||
width: processWidth,
|
||
position: "absolute",
|
||
marginTop: -processHeight / 2,
|
||
left: thumbSize / 2,
|
||
zIndex: 1,
|
||
borderRadius: 50,
|
||
}}
|
||
/>
|
||
<View
|
||
style={{
|
||
backgroundColor: minimumTrackTintColor,
|
||
width: (rightProcess - leftProcess) * processWidth,
|
||
height: processHeight,
|
||
marginLeft: thumbSize / 2 + leftProcess * processWidth,
|
||
position: "absolute",
|
||
marginTop: -processHeight / 2,
|
||
left: 0,
|
||
zIndex: 10,
|
||
borderRadius: 50,
|
||
}}
|
||
/>
|
||
</View>
|
||
{/* 左侧控件 */}
|
||
<View
|
||
transition={1000}
|
||
cachePolicy="disk"
|
||
style={{
|
||
position: "absolute",
|
||
left: leftProcess * processWidth,
|
||
zIndex: 10,
|
||
width: thumbSize,
|
||
height: thumbSize,
|
||
borderRadius: 50,
|
||
backgroundColor: "#ff75c8",
|
||
borderColor: "#ffffff",
|
||
borderWidth: 2,
|
||
}}
|
||
{...leftWatcher.current?.panHandlers}
|
||
></View>
|
||
{/* 右侧控件 */}
|
||
<View
|
||
transition={1000}
|
||
cachePolicy="disk"
|
||
style={{
|
||
position: "absolute",
|
||
left: rightProcess * processWidth,
|
||
zIndex: 10,
|
||
width: thumbSize,
|
||
height: thumbSize,
|
||
backgroundColor: "#fff",
|
||
borderRadius: 50,
|
||
backgroundColor: "#ff75c8",
|
||
borderColor: "#ffffff",
|
||
borderWidth: 2,
|
||
}}
|
||
{...rightWatcher.current?.panHandlers}
|
||
></View>
|
||
|
||
<View
|
||
style={{
|
||
...tailwind("flex flex-row justify-between items-center"),
|
||
position: "absolute",
|
||
top: -54,
|
||
right: 24,
|
||
}}
|
||
>
|
||
{unitSite == "left" && (
|
||
<Text
|
||
style={{
|
||
...tailwind("text-white text-lg"),
|
||
maxWidth: thumbSize * 2,
|
||
color: "#ff75c8",
|
||
}}
|
||
>
|
||
{unit}
|
||
</Text>
|
||
)}
|
||
<Text
|
||
style={{
|
||
...tailwind("text-white text-lg"),
|
||
color: "#ff75c8",
|
||
}}
|
||
>
|
||
{hasInfinity && leftProcess == 1 ? ">" + upper_bound : leftValue}
|
||
</Text>
|
||
<Text
|
||
style={{
|
||
...tailwind("text-lg"),
|
||
marginHorizontal: 4,
|
||
color: "#ff75c8",
|
||
}}
|
||
>
|
||
~
|
||
</Text>
|
||
<Text
|
||
style={{
|
||
...tailwind("text-white text-lg"),
|
||
color: "#ff75c8",
|
||
}}
|
||
>
|
||
{hasInfinity && rightProcess >= 1 ? "∞" : rightValue}
|
||
</Text>
|
||
{unitSite == "right" && (
|
||
<Text
|
||
style={{
|
||
...tailwind("text-white text-lg"),
|
||
maxWidth: thumbSize * 2,
|
||
color: "#ff75c8",
|
||
}}
|
||
>
|
||
{unit}
|
||
</Text>
|
||
)}
|
||
</View>
|
||
<View
|
||
style={{
|
||
...tailwind(
|
||
"absolute text-white text-sm flex flex-row justify-between"
|
||
),
|
||
width: width - 22,
|
||
left: 0,
|
||
top: 20,
|
||
color: "#ffffff80",
|
||
}}
|
||
>
|
||
<Text
|
||
style={{
|
||
...tailwind(" text-white text-sm"),
|
||
minWidth: thumbSize,
|
||
maxWidth: thumbSize * 2,
|
||
color: "#ffffff80",
|
||
}}
|
||
>
|
||
{lower_bound}
|
||
</Text>
|
||
<Text
|
||
style={{
|
||
...tailwind(" text-white text-sm"),
|
||
minWidth: thumbSize,
|
||
color: "#ffffff80",
|
||
}}
|
||
>
|
||
{hasInfinity ? "∞" : upper_bound}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
);
|
||
}
|