diff --git a/docs/blog/switch.md b/docs/blog/switch.md new file mode 100644 index 000000000000..0fdd7c690441 --- /dev/null +++ b/docs/blog/switch.md @@ -0,0 +1,40 @@ +--- +slug: switch +title: Switch +--- + +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. + +import Switch from '@site/static/examples/Switch'; +import SwitchSrc from '!!raw-loader!@site/static/examples/Switch'; +import ExampleVideo from '@site/src/components/ExampleVideo'; +import CollapsibleCode from '@site/src/components/CollapsibleCode'; + +} /> + +The following implementation of a switch relies on [animatable values](/docs/fundamentals/glossary#animatable-value). Leveraging animatable values of color and position enables smooth transition between the two states. + +Switch + + + + + +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. + + + +The values are updated during the `onLayout` event of the element. + + + +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. + +Switch + + diff --git a/docs/static/examples/Switch.js b/docs/static/examples/Switch.js new file mode 100644 index 000000000000..f8311bff9282 --- /dev/null +++ b/docs/static/examples/Switch.js @@ -0,0 +1,120 @@ +import React from 'react'; +import { + Pressable, + SafeAreaView, + View, + StyleSheet, + Button, +} from 'react-native'; +import Animated, { + interpolate, + interpolateColor, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; + +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 ( + + { + height.value = e.nativeEvent.layout.height; + width.value = e.nativeEvent.layout.width; + }} + style={[switchStyles.track, style, trackAnimatedStyle]}> + + + + ); +}; + +const switchStyles = StyleSheet.create({ + track: { + alignItems: 'flex-start', + width: 100, + height: 40, + padding: 5, + }, + thumb: { + height: '100%', + aspectRatio: 1, + backgroundColor: 'white', + }, +}); + +export default function App() { + const isOn = useSharedValue(false); + + const handlePress = () => { + isOn.value = !isOn.value; + }; + + return ( + + + +