我使用 reanimated 切换功能创建了一个自定义开关组件,它工作正常,它切换并调用 onPress 函数,该函数交替返回 true 或 false。
我想在initiState通过状态或父级的新道具更改时为切换效果设置动画,请帮助,谢谢。
开关.tsx
import React, { useMemo } from 'react';
import { StyleSheet, Dimensions } from 'react-native';
import { timing, bInterpolate } from 'react-native-redash';
import { TapGestureHandler, State } from 'react-native-gesture-handler';
import Animated, { Easing } from 'react-native-reanimated';
interface SwitchProps {
onPress: (data: any) => void;
color: string;
background: string;
initState: boolean;
change: any;
}
const { height } = Dimensions.get('window');
const size = {
width: height >= 812 ? 60 : 50,
height: height >= 812 ? 35 : 30,
};
const {
Value,
useCode,
block,
cond,
eq,
set,
Clock,
clockRunning,
event,
sub,
stopClock,
onChange,
call,
and,
not,
} = Animated;
const Switch = ({
color,
onPress,
background: backgroundColor,
initState,
}: SwitchProps) => {
const { state, isOn, animate, shouldAnimate, width, clock } = useMemo(
() => ({
state: new Value(State.UNDETERMINED),
isOn: new Value(initState ? 1 : 0),
animate: new Value(initState ? 1 : 0),
clock: new Clock(),
shouldAnimate: new Value(0),
width: new Value(0),
}),
[]
);
useCode(() => {
return block([
cond(eq(state, State.END), set(shouldAnimate, 1)),
onChange(
state,
cond(
and(eq(state, State.END), not(clockRunning(clock))),
cond(
eq(isOn, 1),
call([], () => onPress(false)),
call([], () => onPress(true))
)
)
),
cond(and(eq(shouldAnimate, 1), eq(isOn, 0)), [
set(
animate,
timing({
from: animate,
to: 1,
duration: 100,
easing: Easing.linear,
clock,
})
),
cond(not(clockRunning(clock)), [set(shouldAnimate, 0), set(isOn, 1)]),
]),
cond(and(eq(shouldAnimate, 1), eq(isOn, 1)), [
set(
animate,
timing({
from: animate,
to: 0,
duration: 100,
easing: Easing.linear,
clock,
})
),
cond(not(clockRunning(clock)), [set(shouldAnimate, 0), set(isOn, 0)]),
]),
]);
}, [initState]);
const translateX = bInterpolate(animate, 0, sub(size.height - 10, width));
const backgroundWidth = bInterpolate(animate, 0, size.width);
return (
<TapGestureHandler
onHandlerStateChange={event([{ nativeEvent: { state } }])}>
<Animated.View
onLayout={event([
{
nativeEvent: { width },
},
])}
style={{
...size,
borderRadius: size.height / 2,
overflow: 'hidden',
backgroundColor: 'white',
}}>
<Animated.View
style={{
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
backgroundColor,
}}
/>
<Animated.View
style={{
flex: 1,
backgroundColor: color,
width: backgroundWidth,
}}
/>
<Animated.View
style={[styles.circle, { transform: [{ translateX }] }]}
/>
</Animated.View>
</TapGestureHandler>
);
};
const styles = StyleSheet.create({
circle: {
width: size.height - 2,
height: size.height - 2,
position: 'absolute',
left: 1,
top: 1,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,
elevation: 3,
backgroundColor: 'white',
borderRadius: size.height - 2 / 2,
},
});
Switch.defaultProps = {
color: '#00000',
onPress: () => {},
initState: false,
background: '#8c8c8c',
change: [],
};
export default Switch;