Files
workspace/code/allapp/allapp-rn/src/components/common/ao/Switch.tsx
T
2026-03-02 17:29:51 +08:00

128 lines
2.6 KiB
TypeScript

import { useEffect } from 'react'
import { Pressable, StyleSheet } from 'react-native'
import Animated, {
interpolateColor,
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated'
interface SwitchProps {
value: any
onChange: (val: any) => void
trueValue?: any
falseValue?: any
width?: number
height?: number
activeColor?: string
inactiveColor?: string
duration?: number
disabled?: boolean // 新增 disabled
}
export default function Switch({
value,
onChange,
trueValue,
falseValue,
width = 52,
height = 24,
activeColor = '#333',
inactiveColor = '#e5e7eb',
duration = 200,
disabled = false, // 默认可用
}: SwitchProps) {
// 默认值 fallback
const trueVal = trueValue ?? true
const falseVal = falseValue ?? false
// 当前状态是否为激活
const isActive = Object.is(value, trueVal)
// 控制动画的 sharedValue
const progress = useSharedValue(isActive ? 1 : 0)
useEffect(() => {
progress.value = withTiming(isActive ? 1 : 0, { duration })
}, [isActive, duration])
const knobSize = height - 4
const translateX = width - knobSize - 4
// 动画轨道样式
const trackStyle = useAnimatedStyle(() => {
const baseColor = interpolateColor(
progress.value,
[0, 1],
[inactiveColor, activeColor],
)
return {
backgroundColor: baseColor,
opacity: disabled ? 0.7 : 1, // disabled 半透明
}
})
// 动画滑块样式
const knobStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: progress.value * translateX,
},
],
opacity: disabled ? 0.8 : 1, // knob 半透明
}
})
return (
<Pressable
onPress={() => {
if (disabled)
return
const nextValue = isActive ? falseVal : trueVal
onChange(nextValue)
}}
style={{ width, height }}
>
<Animated.View
style={[
styles.track,
{
width,
height,
borderRadius: height / 2,
},
trackStyle,
]}
>
<Animated.View
style={[
styles.knob,
{
width: knobSize,
height: knobSize,
borderRadius: knobSize / 2,
},
knobStyle,
]}
/>
</Animated.View>
</Pressable>
)
}
const styles = StyleSheet.create({
track: {
justifyContent: 'center',
padding: 2,
},
knob: {
backgroundColor: '#fff',
elevation: 2,
shadowColor: '#000',
shadowOpacity: 0.2,
shadowRadius: 2,
shadowOffset: { width: 0, height: 1 },
},
})