//Reanimated v4
function App() {
const sharedScale = useSharedValue(1); //1. create one or more shared values
const sharedOpacity = useSharedValue(1);
//2. create an animated style to be used by <Reanimated.View>
const animatedStyle = useAnimatedStyles(() => {
return {
transform: [{ scale: withDelay(1500, withTiming(sharedScale.value, { duration: 800 })) }, { skewX: withTiming() }, { rotateZ: withTiming() }],
opacity: withTiming(sharedOpacity.value, { duration: 2000 }),
};
});
// withDelay, withTiming, withSequence
//3. Add reanimated style to reanimated view
//4. Add trigger(s) to change the shared value(s)
return (
<Reanimated.View style={[animatedStyle, styles.x]}>
<Button
onPress={() => {
//run animation by changing the shared value(s)
sharedScale.set(2);
sharedOpacity.set(0.5);
}}
title="Click me"
/>
</Reanimated.View>
);
}
Last active
September 24, 2025 16:51
-
-
Save prof3ssorSt3v3/267fefde16a1184b8fbfe2a99dac168b to your computer and use it in GitHub Desktop.
React Native Gesture Handling and Animations Starter Files
This file contains hidden or 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
| import { StatusBar } from 'expo-status-bar'; | |
| import { useRef } from 'react'; | |
| import { StyleSheet, Text, View, Button, Animated } from 'react-native'; | |
| export default function Animation() { | |
| // const sv = useSharedValue(0); | |
| const av = new Animated.Value(0); | |
| // Animated.Value() is for a single value. | |
| // Animated.ValueXY() will manage two values. | |
| const myAnimatedValue = useRef(av).current; | |
| const moveRight = () => { | |
| Animated.timing(myAnimatedValue, { | |
| toValue: 180, | |
| duration: 500, | |
| useNativeDriver: true, | |
| }).start(); | |
| //when the moveRight function is called Animated will iterate through | |
| //the possible values from 0 to 200 over the course of 500ms | |
| //the start() method begins the iteration through the values. | |
| //at the end, myAnimatedValue will have a current value of 200. | |
| }; | |
| const moveLeft = () => { | |
| Animated.timing(myAnimatedValue, { | |
| toValue: -180, | |
| delay: 500, | |
| duration: 1000, | |
| useNativeDriver: true, | |
| }).start(); | |
| //reverse of the moveRight function except it takes a full second, and | |
| //has a half second delay before starting. | |
| //at the end myAnim will have a current value of 0 | |
| }; | |
| const reset = () => { | |
| Animated.spring(myAnimatedValue, { | |
| toValue: 0, | |
| delay: 0, | |
| duration: 800, | |
| useNativeDriver: true, | |
| }).start(); | |
| }; | |
| //There are Animated versions of View, Text, Image, ScrollView, FlatList, and SectionList | |
| return ( | |
| <View style={styles.container}> | |
| <Text>Animated</Text> | |
| <Animated.View | |
| style={{ | |
| // Bind paddingStart to animated value | |
| // fontSize: myAnimatedValue, | |
| transform: [ | |
| { translateX: myAnimatedValue }, | |
| { perspective: 1000 }, | |
| // without perspective, transform Animation will not render on Android while working fine on iOS | |
| ], | |
| }} | |
| > | |
| <Text style={styles.txt}>Watch me move</Text> | |
| </Animated.View> | |
| <View> | |
| <Button title="click to move right" onPress={moveRight} /> | |
| <Button title="click to move left" onPress={moveLeft} /> | |
| <Button title="reset position of text" onPress={reset} /> | |
| </View> | |
| <StatusBar style="auto" /> | |
| </View> | |
| ); | |
| } | |
| const styles = StyleSheet.create({ | |
| container: { | |
| flex: 1, | |
| backgroundColor: '#fff', | |
| alignItems: 'center', | |
| justifyContent: 'center', | |
| }, | |
| box: { | |
| padding: 10, | |
| width: 200, | |
| height: 200, | |
| backgroundColor: 'hsl(120, 50%, 70%)', | |
| }, | |
| txt: { | |
| padding: 20, | |
| margin: 20, | |
| fontSize: 20, | |
| }, | |
| }); |
This file contains hidden or 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
| import { StatusBar } from 'expo-status-bar'; | |
| // import { useState } from 'react'; | |
| import { StyleSheet, Text, View } from 'react-native'; | |
| import { GestureHandlerRootView, GestureDetector, Gesture } from 'react-native-gesture-handler'; | |
| import Reanimated, { useSharedValue, useAnimatedStyle } from 'react-native-reanimated'; | |
| export default function App() { | |
| const offset = useSharedValue({ x: 0, y: 0 }); | |
| const start = useSharedValue({ x: 0, y: 0 }); | |
| const scale = useSharedValue(1); | |
| const savedScale = useSharedValue(1); | |
| const rotation = useSharedValue(0); | |
| const savedRotation = useSharedValue(0); | |
| const animatedStyles = useAnimatedStyle(() => { | |
| return { | |
| transform: [{ translateX: offset.value.x }, { translateY: offset.value.y }, { scale: scale.value }, { rotateZ: `${rotation.value}rad` }], | |
| }; | |
| }); | |
| const dragGesture = Gesture.Pan() | |
| .averageTouches(true) | |
| .onUpdate((ev) => { | |
| offset.value = { | |
| x: ev.translationX + start.value.x, | |
| y: ev.translationY + start.value.y, | |
| }; | |
| }) | |
| .onEnd(() => { | |
| start.value = { | |
| x: offset.value.x, | |
| y: offset.value.y, | |
| }; | |
| }); | |
| const zoomGesture = Gesture.Pinch() | |
| .onUpdate((ev) => { | |
| scale.value = savedScale.value * ev.scale; | |
| }) | |
| .onEnd(() => { | |
| savedScale.value = scale.value; | |
| }); | |
| const rotateGesture = Gesture.Rotation() | |
| .onUpdate((ev) => { | |
| rotation.value = savedRotation.value + ev.rotation; | |
| }) | |
| .onEnd(() => { | |
| savedRotation.value = rotation.value; | |
| }); | |
| const composed = Gesture.Simultaneous(dragGesture, Gesture.Simultaneous(zoomGesture, rotateGesture)); | |
| return ( | |
| <GestureHandlerRootView> | |
| <View style={styles.container}> | |
| <GestureDetector gesture={composed}> | |
| <Reanimated.View style={[styles.box, animatedStyles]}> | |
| <Text>This is the animated.view</Text> | |
| <Text>Inside the GestureDetector</Text> | |
| </Reanimated.View> | |
| </GestureDetector> | |
| </View> | |
| </GestureHandlerRootView> | |
| ); | |
| } | |
| // </GestureDetector> | |
| const styles = StyleSheet.create({ | |
| container: { | |
| flex: 1, | |
| backgroundColor: '#fff', | |
| alignItems: 'center', | |
| justifyContent: 'center', | |
| }, | |
| box: { | |
| padding: 10, | |
| width: 200, | |
| height: 200, | |
| backgroundColor: 'hsl(120, 50%, 70%)', | |
| }, | |
| }); |
This file contains hidden or 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
| import { StatusBar } from 'expo-status-bar'; | |
| import { useState } from 'react'; | |
| import { StyleSheet, Text, View, Pressable } from 'react-native'; | |
| import Reanimated, { useSharedValue, useAnimatedStyle, withTiming, withDelay, withRepeat, withSequence, FadeInRight, FadeOutLeft, Easing } from 'react-native-reanimated'; | |
| export default function Reanimation() { | |
| const scaleXY = useSharedValue(1); | |
| const [scale, setScale] = useState(1); | |
| const animatedStyles = useAnimatedStyle(() => ({ | |
| transform: [{ scale: withTiming(scaleXY.value) }], | |
| })); // jump immediately to the new value | |
| const animatedStyles2 = useAnimatedStyle(() => { | |
| return { | |
| transform: [{ scale: withSequence(withRepeat(withDelay(1000, withTiming(scaleXY.value, { duration: 1000 })), 2), withTiming(0.6, { duration: 900 })) }], | |
| }; | |
| }); //take 800ms to move to the new value | |
| return ( | |
| <View style={styles.container}> | |
| <Reanimated.View style={[styles.box, animatedStyles2]}> | |
| <Text>This is the animated.view</Text> | |
| <Text>{scale}</Text> | |
| </Reanimated.View> | |
| <View> | |
| <Pressable | |
| style={{ marginBlock: 20, paddingBlock: 20, paddingInline: 10, borderWidth: 1, borderRadius: 10, borderColor: 'steelblue', color: 'steelblue', backgroundColor: '#fff' }} | |
| onPress={() => { | |
| let num = Math.random() + 0.5; /* 0.5 to 1.4999 */ | |
| setScale(num); | |
| scaleXY.set(num); | |
| //increase the scale value by 10% with each tap | |
| }} | |
| > | |
| <Text>Click to Animate</Text> | |
| </Pressable> | |
| </View> | |
| {[0, 1, 2, 3, 4, 5].map((item) => ( | |
| <Reanimated.View | |
| style={styles.entering} | |
| entering={FadeInRight.delay(500 * item) | |
| .duration(500) | |
| .easing(Easing.inOut(Easing.quad)) | |
| .withInitialValues({ transform: [{ translateX: 1000 }] })} | |
| exiting={FadeOutLeft} | |
| key={`item_${item}`} | |
| > | |
| <Text>Animated Item {item + 1} On To Screen</Text> | |
| </Reanimated.View> | |
| ))} | |
| <StatusBar style="auto" /> | |
| </View> | |
| ); | |
| } | |
| const styles = StyleSheet.create({ | |
| container: { | |
| flex: 1, | |
| backgroundColor: '#fff', | |
| alignItems: 'center', | |
| justifyContent: 'center', | |
| }, | |
| box: { | |
| padding: 10, | |
| width: 200, | |
| height: 200, | |
| backgroundColor: 'hsl(120, 50%, 70%)', | |
| }, | |
| entering: { | |
| backgroundColor: 'lavender', | |
| paddingBlock: 10, | |
| paddingInline: 20, | |
| marginBlock: 5, | |
| }, | |
| }); |
This file contains hidden or 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
| import { Text, StyleSheet, View, Pressable, Button } from 'react-native'; | |
| import { useRef } from 'react'; | |
| import { GestureHandlerRootView } from 'react-native-gesture-handler'; | |
| import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable'; | |
| import Reanimated, { useAnimatedStyle } from 'react-native-reanimated'; | |
| function RightAction(prog, drag) { | |
| const styleAnimation = useAnimatedStyle(() => { | |
| console.log('showRightProgress:', prog.value); | |
| console.log('appliedTranslation:', drag.value); | |
| return { | |
| transform: [{ translateX: drag.value + 50 }], | |
| }; | |
| }); | |
| return ( | |
| <Reanimated.View style={styleAnimation}> | |
| <Pressable | |
| onPress={() => { | |
| console.log('delete this item'); | |
| }} | |
| > | |
| <Text style={styles.rightAction}>Icon</Text> | |
| </Pressable> | |
| </Reanimated.View> | |
| ); | |
| } | |
| export default function Swiping() { | |
| const reanimatedRef = useRef(); | |
| return ( | |
| <GestureHandlerRootView> | |
| <View style={styles.container}> | |
| <ReanimatedSwipeable containerStyle={[styles.swipeable, {}]} friction={2} rightThreshold={40} dragOffsetFromRightEdge={40} renderRightActions={RightAction} ref={reanimatedRef}> | |
| <Text style={styles.txt}>Swipe me!</Text> | |
| </ReanimatedSwipeable> | |
| <Button | |
| onPress={() => { | |
| reanimatedRef.current.reset(); // no animation | |
| // reanimatedRef.current.close(); //runs animation | |
| //reanimatedRef.current!.openRight(); //runs animation | |
| //reanimatedRef.current!.openLeft(); //runs animation | |
| }} | |
| title="Close" | |
| /> | |
| </View> | |
| </GestureHandlerRootView> | |
| ); | |
| } | |
| const styles = StyleSheet.create({ | |
| container: { | |
| flex: 1, | |
| backgroundColor: '#fff', | |
| alignItems: 'stretch', | |
| justifyContent: 'center', | |
| }, | |
| rightAction: { | |
| width: 50, | |
| height: 50, | |
| padding: 5, | |
| backgroundColor: 'gold', | |
| color: 'black', | |
| }, | |
| separator: { | |
| width: '100%', | |
| borderTopWidth: 1, | |
| }, | |
| txt: { color: 'white', paddingBlock: 10 }, | |
| swipeable: { | |
| height: 50, | |
| backgroundColor: 'rebeccapurple', | |
| color: 'white', | |
| alignItems: 'center', | |
| }, | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment