-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Slider example to Reanimated Cookbook (#6094)
We added Slider example to `Reanimated Cookbook` Result: <img width="800" alt="image" src="https://github.com/software-mansion/react-native-reanimated/assets/59940332/33641f14-50d9-4db0-9544-0dfb65ac893c">
- Loading branch information
1 parent
57b8693
commit 987e017
Showing
4 changed files
with
158 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
--- | ||
slug: slider | ||
title: Slider | ||
--- | ||
|
||
Slider allows users to adjust a value or control a setting by sliding a handle along a track. It is commonly used to adjust settings such as volume, brightness, or in this case, the width of a box. | ||
|
||
import Slider from '@site/static/examples/Slider'; | ||
import SliderSrc from '!!raw-loader!@site/static/examples/Slider'; | ||
import ExampleVideo from '@site/src/components/ExampleVideo'; | ||
import CollapsibleCode from '@site/src/components/CollapsibleCode'; | ||
|
||
<InteractiveExample src={SliderSrc} component={Slider} /> | ||
|
||
We use the `useSharedValue` hook to store the offset of the slider handle, allowing for smooth animation during sliding. | ||
|
||
<CollapsibleCode src={SliderSrc} showLines={[18,18]}/> | ||
|
||
This example is done using [Pan gesture](https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/pan-gesture) from `react-native-gesture-handler` library. It adjusts the handle's position and width of the box accordingly to the current offset. The offset is a [shared value](/docs/fundamentals/glossary#shared-value) and is updated during the `onChange` event of the pan gesture. | ||
|
||
<samp id="Slider">Slider</samp> | ||
|
||
<CollapsibleCode src={SliderSrc} showLines={[28,41]}/> | ||
|
||
<ExampleVideo | ||
sources={{ | ||
android: "/react-native-reanimated/recordings/examples/slider_android.mov", | ||
ios: "/react-native-reanimated/recordings/examples/slider_ios.mov" | ||
}} | ||
/> | ||
|
||
The `useAnimatedStyle` hook is used to create animated styles for both the box and the slider handle. This ensures that changes to the offset value result in smooth animations for both components. | ||
|
||
<samp id="Slider">Slider</samp> | ||
|
||
<CollapsibleCode src={SliderSrc} showLines={[40,50]}/> | ||
|
||
Leveraging animated props allows us to run them on the UI thread instead of the JS thread. To prevent unnecessary re-renders when the text displaying the current width of the box changes, we used the `useAnimatedProps` hook. | ||
|
||
Additionally, we opted for **TextInput** instead of **Text** because **TextInput** has a `value` property that can be animated, whereas **Text** only has children. | ||
|
||
This approach also enabled us to animate **TextInput** using [shared values](fundamentals/glossary#shared-value). | ||
|
||
<samp id="Slider">Slider</samp> | ||
|
||
<CollapsibleCode src={SliderSrc} showLines={[53,59]}/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import React from 'react'; | ||
import { View, StyleSheet } from 'react-native'; | ||
import { TextInput } from 'react-native-gesture-handler'; | ||
import { | ||
GestureHandlerRootView, | ||
GestureDetector, | ||
Gesture, | ||
} from 'react-native-gesture-handler'; | ||
import Animated, { | ||
useSharedValue, | ||
useAnimatedStyle, | ||
useAnimatedProps, | ||
} from 'react-native-reanimated'; | ||
|
||
const INITIAL_BOX_SIZE = 50; | ||
const SLIDER_WIDTH = 300; | ||
|
||
Animated.addWhitelistedNativeProps({ text: true }); | ||
|
||
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); | ||
|
||
const Slider = () => { | ||
const offset = useSharedValue(0); | ||
const boxWidth = useSharedValue(INITIAL_BOX_SIZE); | ||
const MAX_VALUE = SLIDER_WIDTH - INITIAL_BOX_SIZE; | ||
|
||
const pan = Gesture.Pan().onChange((event) => { | ||
offset.value = | ||
Math.abs(offset.value) <= MAX_VALUE | ||
? offset.value + event.changeX <= 0 | ||
? 0 | ||
: offset.value + event.changeX >= MAX_VALUE | ||
? MAX_VALUE | ||
: offset.value + event.changeX | ||
: offset.value; | ||
|
||
const newWidth = INITIAL_BOX_SIZE + offset.value; | ||
boxWidth.value = newWidth; | ||
}); | ||
|
||
const boxStyle = useAnimatedStyle(() => { | ||
return { | ||
width: INITIAL_BOX_SIZE + offset.value, | ||
}; | ||
}); | ||
|
||
const sliderStyle = useAnimatedStyle(() => { | ||
return { | ||
transform: [{ translateX: offset.value }], | ||
}; | ||
}); | ||
|
||
const animatedProps = useAnimatedProps(() => { | ||
return { | ||
text: `Box width: ${Math.round(boxWidth.value)}`, | ||
defaultValue: `Box width: ${boxWidth.value}`, | ||
}; | ||
}); | ||
|
||
return ( | ||
<GestureHandlerRootView style={styles.container}> | ||
<AnimatedTextInput | ||
animatedProps={animatedProps} | ||
style={styles.boxWidthText} | ||
editable={false} | ||
/> | ||
<Animated.View style={[styles.box, boxStyle]} /> | ||
<View style={styles.sliderTrack}> | ||
<GestureDetector gesture={pan}> | ||
<Animated.View style={[styles.sliderHandle, sliderStyle]} /> | ||
</GestureDetector> | ||
</View> | ||
</GestureHandlerRootView> | ||
); | ||
}; | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: 1, | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
gap: 32, | ||
}, | ||
sliderTrack: { | ||
width: SLIDER_WIDTH, | ||
height: 50, | ||
backgroundColor: '#82cab2', | ||
borderRadius: 25, | ||
justifyContent: 'center', | ||
padding: 5, | ||
}, | ||
sliderHandle: { | ||
width: 40, | ||
height: 40, | ||
backgroundColor: '#f8f9ff', | ||
borderRadius: 20, | ||
position: 'absolute', | ||
left: 5, | ||
}, | ||
box: { | ||
height: INITIAL_BOX_SIZE, | ||
backgroundColor: '#b58df1', | ||
borderRadius: 10, | ||
}, | ||
boxWidthText: { | ||
textAlign: 'center', | ||
fontSize: 18, | ||
color: '#001a72', | ||
}, | ||
}); | ||
|
||
export default Slider; |
Binary file added
BIN
+2.55 MB
packages/docs-reanimated/static/recordings/examples/slider_android.mov
Binary file not shown.
Binary file not shown.