A switch element is a user interface component that allows users to toggle between two or more states. It is commonly used to turn on/off a setting, enable/disable a feature, or select between options.
The following implementation of a switch relies on animatable values. Leveraging animatable values of color and position enables smooth transition between the two states.
Switch const trackAnimatedStyle = useAnimatedStyle(() => {
const color = interpolateColor(
value.value,
[0, 1],
[trackColors.off, trackColors.on]
);
const colorValue = withTiming(color, { duration });
return {
backgroundColor: colorValue,
borderRadius: height.value / 2,
};
});
const thumbAnimatedStyle = useAnimatedStyle(() => {
const moveValue = interpolate(
Number(value.value),
[0, 1],
[0, width.value - height.value]
);
const translateValue = withTiming(moveValue, { duration });
return {
transform: [{ translateX: translateValue }],
borderRadius: height.value / 2,
};
});
We use the useSharedValue
hook to store the dimensions of the element, which allows for precise calculation of position changes during the animation. The hook is there to prevent unnecessary re-renders.
const height = useSharedValue(0);
const width = useSharedValue(0);
The values are updated during the onLayout
event of the element.
<Animated.View
onLayout={(e) => {
height.value = e.nativeEvent.layout.height;
width.value = e.nativeEvent.layout.width;
}}
style={[switchStyles.track, style, trackAnimatedStyle]}>
The Switch component can represent any boolean value passed as a prop. The state dynamically adjusts based on the value
prop resulting in smooth transition animations. It enables passing any function using the onPress
prop. The duration
prop controls the duration of the animation. The style
and trackColors
props enable personalization.
const Switch = ({
value,
onPress,
style,
duration = 400,
trackColors = { on: '#82cab2', off: '#fa7f7c' },
}) => {
const height = useSharedValue(0);
const width = useSharedValue(0);
const trackAnimatedStyle = useAnimatedStyle(() => {
const color = interpolateColor(
value.value,
[0, 1],
[trackColors.off, trackColors.on]
);
const colorValue = withTiming(color, { duration });
return {
backgroundColor: colorValue,
borderRadius: height.value / 2,
};
});
const thumbAnimatedStyle = useAnimatedStyle(() => {
const moveValue = interpolate(
Number(value.value),
[0, 1],
[0, width.value - height.value]
);
const translateValue = withTiming(moveValue, { duration });
return {
transform: [{ translateX: translateValue }],
borderRadius: height.value / 2,
};
});
return (
<Pressable onPress={onPress}>
<Animated.View
onLayout={(e) => {
height.value = e.nativeEvent.layout.height;
width.value = e.nativeEvent.layout.width;
}}
style={[switchStyles.track, style, trackAnimatedStyle]}>
<Animated.View
style={[switchStyles.thumb, thumbAnimatedStyle]}></Animated.View>
</Animated.View>
</Pressable>
);
};